Oracle® Database

データ・ウェアハウス・ガイド

11gリリース2 (11.2)

B56309-04(原本部品番号:E25554-02)

2013年7月

Oracle Databaseデータ・ウェアハウス・ガイド, 11gリリース2(11.2)

B56309-04

Copyright © 2001, 2013, Oracle and/or its affiliates.All rights reserved.

原本著者: Paul Lane

原本協力者:  Patrick Amor, Hermann Baer, Mark Bauer, Subhransu Basu, Srikanth Bellamkonda, Randy Bello, Paula Bingham, Tolga Bozkaya, Lucy Burgess, Donna Carver, Rushan Chen, Benoit Dageville, John Haydu, Lilian Hobbs, Hakan Jakobsson, George Lumpkin, Alex Melidis, Valarie Moore, Cetin Ozbutun, Ananth Raghavan, Jack Raitto, Ray Roccaforte, Sankar Subramanian, Gregory Smith, Margaret Taft, Murali Thiyagarajan, Ashish Thusoo, Thomas Tong, Mark Van de Wiel, Jean-Francois Verrier, Gary Vincent, Andreas Walter, Andy Witkowski, Min Xiao, Tsae-Feng Yu

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクル社までご連絡ください。

このソフトウェアまたは関連ドキュメントが、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供される場合は、次のNoticeが適用されます。

U.S. GOVERNMENT RIGHTS Programs, software, databases, and related documentation and technical data delivered to U.S. Government customers are "commercial computer software" or "commercial technical data" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations.As such, the use, duplication, disclosure, modification, and adaptation shall be subject to the restrictions and license terms set forth in the applicable Government contract, and, to the extent applicable by the terms of the Government contract, the additional rights set forth in FAR 52.227-19, Commercial Computer Software License (December 2007).Oracle USA, Inc., 500 Oracle Parkway, Redwood City, CA 94065.

このソフトウェアまたはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアまたはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用する際、このソフトウェアまたはハードウェアを安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、オラクル社およびその関連会社は一切の責任を負いかねます。

OracleはOracle Corporationおよびその関連企業の登録商標です。その他の名称は、それぞれの所有者の商標または登録商標です。

このソフトウェアおよびドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。オラクル社およびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。オラクル社およびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても、一切の責任を負いかねます。

目次

タイトルおよび著作権情報

はじめに

- 対象読者

- ドキュメントのアクセシビリティについて

- 関連ドキュメント

- 表記規則

Oracle Databaseの新機能

- Oracle Database 11gリリース2(11.2)でのデータ・ウェアハウスの新機能

- Oracle Database 11gリリース1(11.1)でのデータ・ウェアハウスの新機能

第I部 概念

1 データ・ウェアハウスの概要

- データ・ウェアハウスの概要

- サブジェクト指向

- 統合化

- 恒常的

- 時系列

- OLTPとデータ・ウェアハウス環境の比較

- データ・ウェアハウス・アーキテクチャ

- データ・ウェアハウス・アーキテクチャ: 基本

- データ・ウェアハウス・アーキテクチャ: ステージング・エリアを伴う

- データ・ウェアハウス・アーキテクチャ: ステージング・エリアおよびデータ・マートを伴う

- データ・ウェアハウスからの情報の抽出

- OLAP

- 多次元テクノロジの完全統合

- アプリケーション開発の容易性

- 管理の容易性

- セキュリティ

- 優れたパフォーマンスとスケーラビリティ

- コストの削減

- ディメンション・オブジェクトの問合せ

- サマリー・データの効率的な格納および均一な可用性

- ディメンション・オブジェクトの作成および管理のためのツール

- データ・マイニング

- Oracle Data Miningの機能

- Oracle Data Miningのインタフェース

第II部 論理設計

2 データ・ウェアハウスの論理設計

- データ・ウェアハウスの論理設計と物理設計の比較

- 論理設計の作成

- データ・ウェアハウス・スキーマ

- スター・スキーマ

- その他のデータ・ウェアハウス・スキーマ

- データ・ウェアハウス・オブジェクト

- データ・ウェアハウス・オブジェクト: ファクト表

- ファクト表の要件

- データ・ウェアハウス・オブジェクト: ディメンション表

- 階層

- 典型的なディメンション階層

- データ・ウェアハウス・オブジェクト: 一意識別子

- データ・ウェアハウス・オブジェクト: リレーションシップ

- データ・ウェアハウスのオブジェクトとそのリレーションシップの例

第III部 物理設計

3 データ・ウェアハウスの物理設計

- 論理設計から物理設計への変換

- 物理設計

- 物理設計の構造

- 表領域

- 表とパーティション表

- 表の圧縮

- ビュー

- 整合性制約

- 索引とパーティション索引

- マテリアライズド・ビュー

- ディメンション

4 データ・ウェアハウスにおけるハードウェアおよびI/Oの考慮事項

- データ・ウェアハウスにおけるハードウェアおよびI/Oの考慮事項の概要

- 容量ではなく帯域幅に基づくI/Oの構成

- 幅広いストライプ化

- 冗長性の使用

- データベース構築前のI/Oシステムのテスト

- システム拡張のための計画

- 記憶域管理

5 データ・ウェアハウスにおけるパーティション化

- データ・ウェアハウスにおけるパーティション化の概要

6 データ・ウェアハウスにおけるパラレル実行

- パラレル実行の概要

- パラレル実行を使用する理由

- パラレル実行を実装する場合

- パラレル実行を実装しない場合

- 自動並列度および文のキューイング

- メモリー内パラレル実行

7 索引

- データ・ウェアハウスでのビットマップ索引の使用

- データ・ウェアハウス・アプリケーションに対するメリット

- カーディナリティ

- ビットマップ索引の使用対象の判定方法

- ビットマップ索引およびNULL

- パーティション表のビットマップ索引

- データ・ウェアハウスでのビットマップ結合索引の使用

- ビットマップ結合索引の4つの結合モデル

- ビットマップ結合索引の制限事項と要件

- データ・ウェアハウスでのBツリー索引の使用

- 索引の圧縮の使用

- ローカル索引とグローバル索引の選択基準

8 整合性制約

- データ・ウェアハウスで整合性制約が効果的な理由

- 制約の状態の概要

- 一般的なデータ・ウェアハウスの整合性制約

- データ・ウェアハウスでの一意制約

- データ・ウェアハウスでの外部キー制約

- RELY制約

- NOT NULL制約

- 整合性制約およびパラレル化

- 整合性制約およびパーティション化

- ビューの制約

9 基本的なマテリアライズド・ビュー

- マテリアライズド・ビューを使用したデータ・ウェアハウスの概要

- データ・ウェアハウスでのマテリアライズド・ビュー

- 分散コンピューティングでのマテリアライズド・ビュー

- モバイル・コンピューティングでのマテリアライズド・ビュー

- マテリアライズド・ビューの必要性

- サマリー管理のコンポーネント

- データ・ウェアハウスの用語

- マテリアライズド・ビューのスキーマ・デザイン

- スキーマとディメンション表

- マテリアライズド・ビューのスキーマ・デザイン・ガイドライン

- データ・ウェアハウスへのデータのロード

- マテリアライズド・ビューの管理作業の概要

- マテリアライズド・ビューのタイプ

- 集計を含むマテリアライズド・ビュー

- 集計を含むマテリアライズド・ビューの使用要件

- 結合のみを含むマテリアライズド・ビュー

- マテリアライズド結合ビューのFROM句に関する考慮事項

- ネステッド・マテリアライズド・ビュー

- ネステッド・マテリアライズド・ビューを使用する理由

- 結合および集計を含むマテリアライズド・ビューのネスト

- ネステッド・マテリアライズド・ビューの使用上のガイドライン

- ネステッド・マテリアライズド・ビューの使用上の制限

- マテリアライズド・ビューの作成

- 列の別名リストを含むマテリアライズド・ビューの作成

- マテリアライズド・ビューの名前付け

- 記憶域および表の圧縮

- 作成方法

- クエリー・リライトの有効化

- クエリー・リライトの制限

- マテリアライズド・ビューの制限

- 一般的なクエリー・リライトの制限

- リフレッシュ・オプション

- 高速リフレッシュにおける一般的な制限

- 結合のみを含むマテリアライズド・ビューの高速リフレッシュに関する制限

- 集計を含むマテリアライズド・ビューの高速リフレッシュに関する制限

- UNION ALLを含むマテリアライズド・ビューの高速リフレッシュに関する制限

- リフレッシュの目的の達成

- ネステッド・マテリアライズド・ビューのリフレッシュ

- ORDER BY句

- マテリアライズド・ビュー・ログ

- マテリアライズド・ビュー・ログでのFORCEオプションの使用

- マテリアライズド・ビュー・ログのパージ

- Oracle Enterprise Managerの使用

- マテリアライズド・ビューとNLSパラメータの使用

- マテリアライズド・ビューへのコメントの追加

- 既存のマテリアライズド・ビューの登録

- マテリアライズド・ビューに対する索引付けの選択

- マテリアライズド・ビューの削除

- マテリアライズド・ビュー機能の分析

- DBMS_MVIEW.EXPLAIN_MVIEWプロシージャの使用

- DBMS_MVIEW.EXPLAIN_MVIEW宣言

- MV_CAPABILITIES_TABLEの使用

- MV_CAPABILITIES_TABLE.CAPABILITY_NAMEの詳細

- MV_CAPABILITIES_TABLE列の詳細

10 高度なマテリアライズド・ビュー

- パーティション化とマテリアライズド・ビュー

- パーティション・チェンジ・トラッキング

- パーティション・キー

- 結合依存の式

- パーティション・マーカー

- 部分的なリライト

- マテリアライズド・ビューのパーティション化

- 事前作成表のパーティション化

- マテリアライズド・ビューのパーティション化によるメリット

- ローリング・マテリアライズド・ビュー

- 分析処理環境でのマテリアライズド・ビュー

- キューブ

- マテリアライズド・ビューのパーティション化によるメリット

- マテリアライズド・ビューの圧縮

- 集合演算子を含むマテリアライズド・ビュー

- UNION ALLを使用するマテリアライズド・ビューの例

- マテリアライズド・ビューとモデル

- マテリアライズド・ビューの無効化

- マテリアライズド・ビューのセキュリティ問題

- 仮想プライベート・データベース(VPD)を含むマテリアライズド・ビューの問合せ

- 仮想プライベート・データベースを含むクエリー・リライトの使用

- マテリアライズド・ビューおよび仮想プライベート・データベースに関する制限

- マテリアライズド・ビューの変更

11 ディメンション

- ディメンションの概要

- ディメンションの作成

- 属性列の削除および作成

- 複数の階層

- 正規化ディメンション表の使用

- ディメンションの表示

- Oracle Enterprise Managerの使用

- DESCRIBE_DIMENSIONプロシージャの使用

- ディメンションおよび制約の使用

- ディメンションの妥当性チェック

- ディメンションの変更

- ディメンションの削除

第IV部 データ・ウェアハウス環境の管理

12 抽出、変換、ロードの概要

- データ・ウェアハウスにおけるETLの概要

- データ・ウェアハウスにおけるETLの基本

- データの抽出

- データの転送

- データ・ウェアハウスのETLツール

- データ・ウェアハウスにおける日次操作

- データ・ウェアハウスの発展

13 データ・ウェアハウスにおける抽出

- データ・ウェアハウスにおける抽出の概要

- データ・ウェアハウスにおける抽出方法の概要

- 論理的抽出方法

- 全体抽出

- 増分抽出

- 物理的抽出方法

- オンライン抽出

- オフライン抽出

- チェンジ・データ・キャプチャ

- タイムスタンプ

- パーティション化

- トリガー

- データ・ウェアハウスにおける抽出の例

- データファイルを使用した抽出

- SQL*Plusによるフラット・ファイルへの抽出

- OCIまたはPro*Cプログラムによるフラット・ファイルへの抽出

- エクスポート・ユーティリティによるエクスポート・ファイルへの抽出

- 外部表によるエクスポート・ファイルへの抽出

- 分散処理による抽出

14 データ・ウェアハウスにおける転送

- データ・ウェアハウスにおける転送の概要

- データ・ウェアハウスにおける転送メカニズムの概要

- フラット・ファイルを使用した転送

- 分散処理による転送

- トランスポータブル表領域を使用した転送

- トランスポータブル表領域の例

- トランスポータブル表領域の他の用途

15 ロードおよび変換

- データ・ウェアハウスにおけるロードおよび変換の概要

- 変換フロー

- マルチステージ・データ変換

- パイプライン・データ変換

- ステージング領域

- ロード・メカニズム

- SQL*Loaderを使用したデータ・ウェアハウスのロード

- 外部表を使用したデータ・ウェアハウスのロード

- OCIおよびダイレクト・パスAPIを使用したデータ・ウェアハウスのロード

- エクスポート/インポートを使用したデータ・ウェアハウスのロード

- 変換メカニズム

- SQLを使用したデータの変換

- CREATE TABLE ... AS SELECTおよびINSERT /*+APPEND*/ AS SELECT

- UPDATEを使用したデータの変換

- MERGEを使用したデータの変換

- マルチテーブル・インサートを使用したデータの変換

- PL/SQLを使用したデータの変換

- テーブル・ファンクションを使用したデータの変換

- テーブル・ファンクション

- エラーのロギングおよび処理のメカニズム

- ビジネス・ルールの違反

- データ・ルールの違反(データ・エラー)

- PL/SQLを使用したデータ・エラーの処理

- エラー・ロギング表を使用したデータ・エラーの処理

- ロードおよび変換の使用例

- キー参照のシナリオ

- ビジネス・ルール違反のシナリオ

- データ・エラーのシナリオ

- ピボットのシナリオ

16 データ・ウェアハウスのメンテナンス

- パーティション化によるデータ・ウェアハウス・リフレッシュの改善

- リフレッシュの使用例

- データ・ウェアハウスのリフレッシュにパーティション化を使用する使用例

- リフレッシュ使用例1

- リフレッシュ使用例2

- リフレッシュ中のDML操作の最適化

- 効率的なMERGE操作の実装

- 参照整合性の保持

- データの削除

- マテリアライズド・ビューのリフレッシュ

- 完全リフレッシュ

- 高速リフレッシュ

- パーティション・チェンジ・トラッキング(PCT)リフレッシュ

- ON COMMITリフレッシュ

- DBMS_MVIEWパッケージによる手動リフレッシュ

- REFRESHを使用した特定のマテリアライズド・ビューのリフレッシュ

- REFRESH_ALL_MVIEWSを使用したすべてのマテリアライズド・ビューのリフレッシュ

- REFRESH_DEPENDENTを使用した依存マテリアライズド・ビューのリフレッシュ

- リフレッシュへのジョブ・キューの使用

- 高速リフレッシュが可能なパターン

- パラレル化の推奨初期化パラメータ

- リフレッシュの監視

- マテリアライズド・ビューのステータスのチェック

- パーティションの最新状態の表示

- リフレッシュのスケジューリング

- 集計を含むマテリアライズド・ビューのリフレッシュのヒント

- 集計を含まないマテリアライズド・ビューのリフレッシュのヒント

- ネステッド・マテリアライズド・ビューのリフレッシュのヒント

- UNION ALLでの高速リフレッシュのヒント

- コミットSCNベースのマテリアライズド・ビュー・ログを使用した高速リフレッシュのヒント

- マテリアライズド・ビューのリフレッシュ後のヒント

- パーティション表付きマテリアライズド・ビューの使用

- パーティション・チェンジ・トラッキングの高速リフレッシュ

- PCT高速リフレッシュの使用例1

- PCT高速リフレッシュの使用例2

- PCT高速リフレッシュの使用例3

- CONSIDER FRESHの高速リフレッシュ

17 チェンジ・データ・キャプチャ

- チェンジ・データ・キャプチャの概要

- チェンジ・データ・キャプチャを使用しない変更データのキャプチャ

- チェンジ・データ・キャプチャを使用した変更データのキャプチャ

- パブリッシュおよびサブスクライブのモデル

- パブリッシャ

- サブスクライバ

- チェンジ・ソースとチェンジ・データ・キャプチャのモード

- 同期チェンジ・データ・キャプチャ

- 非同期チェンジ・データ・キャプチャ

- 非同期HotLogモード

- 非同期分散HotLogモード

- 非同期AutoLogモード

- チェンジ・セット

- チェンジ・ソースとチェンジ・セットの有効な組合せ

- チェンジ・テーブル

- チェンジ・データ・キャプチャ環境に関する情報の取得

- 変更データの公開準備

- パブリッシャとなるユーザーの作成

- パブリッシャに対する権限およびロールの付与

- パブリッシャのデフォルト表領域の作成

- パスワード・ファイルおよびREMOTE_LOGIN_PASSWORDFILEパラメータの設定

- データをキャプチャするモードの決定

- チェンジ・データ・キャプチャの公開用初期化パラメータの設定

- 同期公開用初期化パラメータ

- 非同期HotLogの公開用初期化パラメータ

- 非同期分散HotLogの公開用初期化パラメータ

- 非同期AutoLogの公開用初期化パラメータ

- Oracle Streamsの値変更時の初期化パラメータ値の調整

- CDC環境に対する変更のトラッキング

- 変更データの公開

- 同期公開の実行

- 非同期HotLogの公開の実行

- 非同期分散HotLogの公開の実行

- 非同期AutoLogの公開の実行

- 変更データのサブスクライブ

- 公開されたデータの管理

- 非同期チェンジ・ソースの管理

- 非同期分散HotLogチェンジ・ソースの有効化および無効化

- 非同期チェンジ・セットの管理

- 開始日および終了日を設定した非同期チェンジ・セットの作成

- 非同期チェンジ・セットの有効化および無効化

- 非同期チェンジ・セットのDDLでのキャプチャの停止

- 非同期チェンジ・セットで戻されたエラーからのリカバリ

- 同期チェンジ・セットの管理

- 同期チェンジ・セットの有効化および無効化

- チェンジ・テーブルの管理

- チェンジ・テーブルの作成

- チェンジ・テーブルの制御列の理解

- TARGET_COLMAP$およびSOURCE_COLMAP$の値の理解

- チェンジ・マーカーの使用

- チェンジ・テーブルへのサブスクライバのアクセスの制御

- チェンジ・テーブルの不要なデータのパージ

- チェンジ・テーブルの削除

- Oracle Data Pumpを使用したチェンジ・データ・キャプチャ・オブジェクトのエクスポートおよびインポート

- チェンジ・データ・キャプチャでOracle Data Pumpを使用する際の制限事項

- Oracle Data Pumpのエクスポートおよびインポートのコマンド例

- チェンジ・テーブルのエクスポートおよびインポートにおけるパブリッシャの考慮事項

- インポート操作後のAutoLogチェンジ・データ・キャプチャ・オブジェクトの再作成

- パブリッシャが変更を行う際のサブスクリプションへの影響

- 同期チェンジ・データ・キャプチャに関する考慮事項

- ダイレクト・パス・インサートの制限

- 同期チェンジ・データ・キャプチャでサポートされるデータ型と表構造

- ごみ箱からソース・テーブルをリストアする際の制限

- 非同期チェンジ・データ・キャプチャに関する考慮事項

- 非同期チェンジ・データ・キャプチャおよびREDOログ・ファイル

- 非同期チェンジ・データ・キャプチャとサプリメンタル・ロギング

- 非同期チェンジ・データ・キャプチャとOracle Streamsコンポーネント

- 非同期チェンジ・データ・キャプチャでサポートされるデータ型と表構造

- NOLOGGINGおよびUNRECOVERABLE操作の制限

- 実装とシステム構成

- Database Configuration Assistantの考慮事項

- サポートされる分散HotLog構成および制限の概要

- ソース・データベースおよびステージング・データベースのOracle Databaseのリリース

- 分散HotLogチェンジ・ソースのリリース11(11.1または11.2)へのアップグレード

- ハードウェア・プラットフォームおよびオペレーティング・システム

- ステージング・データベースにおける複数のパブリッシャの要件

- データベース・リンクの要件

第V部 データ・ウェアハウスのパフォーマンス

18 基本的なクエリー・リライト

- クエリー・リライトの概要

- Oracleによるクエリー・リライト条件

- クエリー・リライトの有効化

- クエリー・リライトの初期化パラメータ

- クエリー・リライトの制御

- クエリー・リライトの精度

- クエリー・リライトの有効化の権限

- サンプル・スキーマおよびマテリアライズド・ビュー

- クエリー・リライトの発生を確認する方法

- クエリー・リライトの例

19 高度なクエリー・リライト

- Oracleによる問合せのリライト方法

- コストベースの最適化

- 一般的なクエリー・リライト方法

- 制約およびディメンションが必要な場合

- クエリー・リライトで行われるチェック

- 結合互換性チェック

- データ充足性チェック

- グルーピング互換性チェック

- 集計可能性チェック

- ディメンションを使用したクエリー・リライト

- ディメンションを使用するメリット

- ディメンションの定義方法

- クエリー・リライトのタイプ

- テキスト一致リライト

- 後戻り結合

- 集計可能性

- 集計ロールアップ

- ディメンションを使用したロールアップ

- マテリアライズド・ビューに含まれるデータのサブセットが1つのみの場合

- クエリー・リライトの定義

- 選択述語のカテゴリ

- クエリー・リライトの選択述語の例

- クエリー・リライトでのHAVING句の処理

- マテリアライズド・ビューにINリストが含まれる場合のクエリー・リライト

- パーティション・チェンジ・トラッキング(PCT)リライト

- レンジ・パーティション表に基づいたPCTリライト

- レンジ-リスト・パーティション表に基づいたPCTリライト

- リスト・パーティション表に基づいたPCTリライト

- PCTリライトとPMARKER

- PMARKERとしてROWIDを使用したPCTリライト

- 複数のマテリアライズド・ビュー

- その他のクエリー・リライトの考慮事項

- ネステッド・マテリアライズド・ビューを使用したクエリー・リライト

- インライン・ビューがある場合のクエリー・リライト

- リモート表を使用したクエリー・リライト

- 表複製がある場合のクエリー・リライト

- デート・フォールディングを使用したクエリー・リライト

- ビューの制約を使用したクエリー・リライト

- ビューの制約の制限

- 集合演算子を含むマテリアライズド・ビューを使用したクエリー・リライト

- UNION ALLマーカー

- グルーピング・セットがある場合のクエリー・リライト

- GROUP BY拡張機能を使用したクエリー・リライト

- 拡張GROUP BYを持つ問合せのヒント

- ウィンドウ関数がある場合のクエリー・リライト

- クエリー・リライトおよび式の一致

- 部分的に失効したマテリアライズド・ビューを使用したクエリー・リライト

- カーソルの共有とバインド変数

- クエリー・リライトでの式の処理

- 同等化を使用した高度なクエリー・リライト

- 同等化を使用した結果キャッシュ・マテリアライズド・ビューの作成

- クエリー・リライトが発生したことの確認

- クエリー・リライトでのEXPLAIN PLANの使用

- クエリー・リライトでのEXPLAIN_REWRITEプロシージャの使用

- DBMS_MVIEW.EXPLAIN_REWRITEの構文

- REWRITE_TABLEの使用

- VARRAYの使用

- EXPLAIN_REWRITEのメリットに関する統計情報

- EXPLAIN_REWRITEでの32KBを超える問合せテキストのサポート

- EXPLAIN_REWRITEおよび複数のマテリアライズド・ビュー

- EXPLAIN_REWRITEの出力

- クエリー・リライトを改善するための設計上の考慮事項

- クエリー・リライトの考慮事項: 制約

- クエリー・リライトの考慮事項: ディメンション

- クエリー・リライトの考慮事項: 外部結合

- クエリー・リライトの考慮事項: テキストの一致

- クエリー・リライトの考慮事項: 集計

- クエリー・リライトの考慮事項: グルーピング条件

- クエリー・リライトの考慮事項: 式の一致

- クエリー・リライトの考慮事項: デート・フォールディング

- クエリー・リライトの考慮事項: 統計情報

- クエリー・リライトの考慮事項: ヒント

- REWRITEヒントおよびNOREWRITEヒント

- REWRITE_OR_ERRORヒント

- 複数のマテリアライズド・ビューでのリライトのヒント

- EXPAND_GSET_TO_UNIONヒント

20 スキーマのモデリング化技法

- データ・ウェアハウスのスキーマ

- 第3正規形

- 第3正規形の問合せの最適化

- スター・スキーマ

- スノーフレーク・スキーマ

- スター・クエリーの最適化

- スター・クエリーのチューニング

- スター型変換の使用

- ビットマップ索引を使用したスター型変換

- ビットマップ索引を使用したスター型変換の実行計画

- ビットマップ結合索引を使用したスター型変換

- ビットマップ結合索引を使用したスター型変換の実行計画

- Oracleによるスター型変換の使用の選択

- スター型変換の制限

21 データ・ウェアハウスにおける集計のためのSQL

- データ・ウェアハウスにおける集計SQLの概要

- 複数ディメンション間の分析

- 最適化されたパフォーマンス

- 集計の使用例

- 例内のNULLの解釈

- ROLLUP(GROUP BYの拡張)

- ROLLUPを使用するとき

- ROLLUPの構文

- 部分的ROLLUP

- CUBE(GROUP BYの拡張)

- CUBEを使用するとき

- CUBEの構文

- 部分的CUBE

- CUBEを使用しない小計の計算

- GROUPING関数

- GROUPING関数

- GROUPINGを使用するとき

- GROUPING_ID関数

- GROUP_ID関数

- GROUPING SETS式

- GROUPING SETSの構文

- 複合列

- 連結グルーピング

- 連結グルーピングと階層的データ・キューブ

- 集計を使用する場合の考慮点

- ROLLUPおよびCUBEでの階層処理

- ROLLUPおよびCUBEでの列の容量

- GROUP BYの拡張機能とともに使用するHAVING句

- GROUP BYの拡張機能とともに使用するORDER BY句

- ROLLUPおよびCUBEとともに他の集計関数を使用する場合

- WITH句を使用した計算

- SQLでの階層的キューブの処理

- SQLでの階層的キューブの指定

- SQLでの階層的キューブの問合せ

- 階層的キューブを格納するマテリアライズド・ビューを作成するSQL

- 階層的キューブのマテリアライズド・ビューの例

22 分析計算およびレポート用SQL関数

- 分析計算およびレポート用SQL関数の概要

- ランキング関数、ウィンドウ関数およびレポート関数

- ランキング

- RANK関数およびDENSE_RANK関数

- ボトムNランキング

- CUME_DIST関数

- PERCENT_RANK関数

- NTILE関数

- ROW_NUMBER関数

- ウィンドウ

- ウィンドウ関数に入力したNULLの取扱

- 論理オフセットを指定したウィンドウ関数

- 集中集計関数

- 重複がある場合の集計ウィンドウ関数

- 行ごとに変動するウィンドウ・サイズ

- 物理オフセットを指定した集計ウィンドウ関数の例

- レポート

- RATIO_TO_REPORT関数

- LAG/LEAD

- LAGおよびLEAD関数の構文

- FIRST_VALUE関数、LAST_VALUE関数およびNTH_VALUE関数

- FIRST_VALUE関数およびLAST_VALUE関数

- NTH_VALUE関数

- 分析用の高度な集計

- LISTAGG関数

- 集計として使用するLISTAGG

- 集計レポートとして使用するLISTAGG

- FIRST/LAST関数

- 通常の集計としてのFIRSTおよびLAST

- 集計レポートとしてのFIRSTおよびLAST

- 逆パーセンタイル

- 通常の集計の構文

- 逆パーセンタイルの例

- 集計レポートとしての使用

- 制限事項

- 仮説ランク

- 線形回帰

- REGR_COUNT関数

- REGR_AVGY関数およびREGR_AVGX関数

- REGR_SLOPE関数およびREGR_INTERCEPT関数

- REGR_R2関数

- REGR_SXX関数、REGR_SYY関数およびREGR_SXY関数

- 線形回帰統計の例

- 線形回帰計算の例

- 統計集計

- 記述統計情報

- 仮説の検証 - パラメトリック検定

- クロス集計統計情報

- 仮説の検証 - ノンパラメトリック検定

- ノンパラメトリック相関

- ユーザー定義集計

- ピボット操作

- 例: ピボット操作

- 複数列に対するピボット操作

- ピボット操作: 複数の集計

- ソース・データ内のNULLとPIVOTで生成されたNULLとの識別

- アンピボット操作

- ワイルド・カードおよび副問合せのXML操作によるピボット操作

- レポート用のデータの稠密化

- パーティション結合の構文

- スパースなデータの例

- データのギャップ補完

- 2つのディメンションのギャップ補完

- 在庫表のギャップ補完

- ギャップを埋めるデータ値の計算

- 稠密化したデータに対する時系列の計算

- 1つの時間レベルでの周期ごとの比較: 例

- 複数の時間レベルでの周期ごとの比較: 例

- ディメンションのカスタム・メンバーの作成: 例

- その他の分析およびレポートの機能

- WIDTH_BUCKET関数

- WIDTH_BUCKETの構文

- 線形代数

- CASE式

- ヒストグラムの作成

- 高頻度項目セット

23 モデリングのSQL

- SQLモデリングの概要

- SQL Modelでのデータの処理方法

- SQLモデリングを使用する理由

- SQLモデリングの機能

- SQLモデリングの基本的なトピック

- ベース・スキーマ

- MODEL句の構文

- SQLモデリングのキーワード

- 値の割当てとNULLの処理

- 計算定義

- セル参照

- シンボリック・ディメンション参照

- 位置ベースのディメンション参照

- ルール

- シングル・セル参照

- 右辺のマルチセル参照

- 左辺のマルチセル参照

- CV関数の使用

- ANYワイルド・カードの使用

- ネステッド・セル参照

- ルールの評価順序

- ルールのグローバルおよびローカル・キーワード

- UPDATE、UPSERTおよびUPSERT ALLの動作

- UPDATEの動作

- UPSERTの動作

- UPSERT ALLの動作

- NULLおよび欠損セルの処理

- 欠損セルとNULLの区別

- 欠損セルおよびNULLのデフォルト値の使用

- セル参照でのNULLの使用

- 参照モデル

- SQLモデリングの高度なトピック

- FORループ

- FORループを含む式の評価

- 反復モデル

- AUTOMATIC ORDERモデルでのルールの依存関係

- 順序付きルール

- 分析関数

- UNIQUE DIMENSIONとUNIQUE SINGLE REFERENCE

- モデリング用SQLを使用する場合の規則および制限事項

- SQLモデリングのパフォーマンスに関する考慮事項

- パラレル実行

- 集計計算

- EXPLAIN PLANを使用したモデル問合せの理解

- ORDERED FASTの使用例

- ORDEREDの使用例

- ACYCLIC FASTの使用例

- ACYCLICの使用例

- CYCLICの使用例

- SQLモデリングの例

24 高度なビジネス・インテリジェンス問合せ

- ビジネス・インテリジェンス問合せの例

用語集

索引

はじめに

「はじめに」では、次の項目について説明します。

- 対象読者

- ドキュメントのアクセシビリティについて

- 関連ドキュメント

- 表記規則

対象読者

このマニュアルは、データ・ウェアハウスを設計、メンテナンスおよび使用するデータベース管理者、システム管理者およびデータベース・アプリケーション開発者を対象としています。

このマニュアルを使用するには、リレーショナル・データベースの概念、Oracle Serverの基本概念、およびOracleを実行するオペレーティング・システム環境について詳しく理解していることを前提としています。

ドキュメントのアクセシビリティについて

オラクル社のアクセシビリティへの取組みの詳細は、Oracle Accessibility ProgramのWebサイトhttp://www.oracle.com/pls/topic/lookup?ctx=acc&id=docaccを参照してください。

Oracleサポートへのアクセス

Oracleカスタマは、My Oracle Supportから電子サポートにアクセスできます。詳細は、http://www.oracle.com/pls/topic/lookup?ctx=acc&id=infoまたはhttp://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs (聴覚障害者向け)を参照してください。

関連ドキュメント

このマニュアルに記載されている例の多くは、Oracleのインストール時にデフォルトでインストールされるシード・データベースのサンプル・スキーマを使用しています。これらのスキーマがどのように作成され、ユーザーがどのように使用できるかについては、『Oracle Databaseサンプル・スキーマ』を参照してください。

このマニュアルは、データ・ウェアハウスに関する標準テキストを補足するものです。Oracle固有の性能を中心に説明しており、データ・ウェアハウスの一般的な性能について詳細に説明するものではありません。追加情報は、次の文書を参照してください。

- 『The Data Warehouse Toolkit』(Ralph Kimball著、John Wiley and Sons, 1996)

- 『Building the Data Warehouse』(William Inmon著、John Wiley and Sons, 1996)

表記規則

このマニュアルでは次の表記規則を使用します。

規則

意味

太字

太字は、操作に関連するGraphical User Interface要素、または本文中で定義されている用語および用語集に記載されている用語を示します。

イタリック体

イタリックは、ドキュメントのタイトル、強調またはユーザーが特定の値を指定するプレースホルダ変数を示します。

固定幅フォント

固定幅フォントは、段落内のコマンド、URL、サンプル内のコード、画面に表示されるテキスト、または入力するテキストを示します。

Oracle Databaseの新機能

この項では、Oracle Database 11gリリース2(11.2)の新機能を説明し、詳細情報の参照先を示します。現在のリリースに移行するユーザーのために、以前のリリースでの新機能に関する情報も含まれています。

次の項で、Oracle Databaseの新機能について説明します。

- Oracle Database 11gリリース2(11.2)でのデータ・ウェアハウスの新機能

- Oracle Database 11gリリース1(11.1)でのデータ・ウェアハウスの新機能

Oracle Database 11gリリース2(11.2)でのデータ・ウェアハウスの新機能

- 分析関数
あるグループ内のメジャー値をリスト(または連結)できる新しいSQL分析関数(LISTAGG)が導入されました。もう1つの新しい関数(NTH_VALUE)では、ウィンドウ内の任意の(つまりn番目の)レコードを取得することが可能です。さらに、既存の関数LAGおよびLEADが改良され、IGNORE NULLSオプションを使用できるようになりました。

関連項目:
詳細は、第22章「分析計算およびレポート用SQL関数」を参照してください。

- 外部表からのファイルの事前処理
ファイルを処理する実行対象のプログラムを指定し、その出力をOracle Databaseで使用できるようになりました。このファイルの事前処理により、大量の圧縮データをロードする際、先にそのデータをディスクに展開する必要がなくなります。

関連項目:
詳細は、第15章「ロードおよび変換」を参照してください。

- マテリアライズド・ビューのリフレッシュの拡張
マテリアライズド・ビュー・ログをリフレッシュ・プロセス外でパージできるようになったため、パフォーマンスが向上します。さらに、集計、結合、もしくはその両方を格納するマテリアライズド・ビューでもパフォーマンスが向上しました。WITH COMMIT SCN句を使用すると、マテリアライズド・ビュー・ログの処理を最適化できます。これにより、リフレッシュ・プロセスが速くなります。

関連項目:
詳細は、第9章「基本的なマテリアライズド・ビュー」および第16章「データ・ウェアハウスのメンテナンス」を参照してください。

Oracle Database 11gリリース1(11.1)でのデータ・ウェアハウスの新機能

- ピボット演算子およびアンピボット演算子
PIVOT演算子を使用すると、複数の行をレポートに有効なコンパクトな結果セットに要約するクロス集計出力を容易に作成できます。たとえば、1か月分の売上が行ごとに整理されている入力データを、各列を1か月分として、1行に12か月分が含まれるように出力できます。またPIVOTでは、複数の入力行が1行に結合されて出力されるので、表を自己結合することなく行間の比較が可能になります。UNPIVOT演算子を使用すると、データを別のリレーショナル操作に有効なフォーマットに変形させることができます。たとえば、ソース・データ・セットの各行に12か月分の売上値が含まれている場合、UNPIVOTを使用すると、各ソース行を、それぞれ1か月分の売上データを含む12の出力行に変形できます。アンピボットされた結果は、ソース・データよりも正規化されたリレーショナル形式になり、より簡潔で効率的なSQLで操作できるようになります。

関連項目:
詳細は、第21章「データ・ウェアハウスにおける集計のためのSQL」を参照してください。

- パーティション・アドバイザ
SQLアクセス・アドバイザは、パーティション化のアドバイスもできるように拡張されました。アプリケーションから最大のパフォーマンスを得るためには、表、索引およびマテリアライズド・ビューのパーティション化を正しく行うことをお薦めします。

関連項目:
詳細は、第5章「データ・ウェアハウスにおけるパーティション化」を参照してください。

- チェンジ・データ・キャプチャ(CDC)の拡張
CDCは、ダイレクト・パス・ロード操作と、パーティション・メンテナンス操作による暗黙的なデータ変更とを認識できるようになりました。ユーザーは必要に応じて、同期CDCをオンおよびオフにできます。また、チェンジ・テーブルから変更データをパージする際の柔軟性も向上し、日付範囲を指定してデータをパージできるようになりました。

さらに、データを変更するためのサブスクリプション・ウィンドウのメンテナンスが容易になりました。チェンジ・サブスクリプションの定義を制御できるようになったため、ウィンドウを前後に移動できます。

関連項目:
詳細は、第17章「チェンジ・データ・キャプチャ」を参照してください。

- クエリー・リライトの拡張
クエリー・リライトは、インライン・ビューを含む問合せをサポートするように拡張されました。このリリースより前は、インライン・ビューを含む問合せはテキストがマテリアライズド・ビュー内のインライン・ビューと完全一致した場合にしかリライトできませんでした。インライン・ビューは問合せとマテリアライズド・ビューが文字どおりに一致する必要がなくなったため、より多くのインライン・ビューを含む問合せをリライトできるようになりました。また、新たに改良されたクエリー・リライトの重要な機能として、リモート表を参照している問合せをリライトできるようになったという点もあげられます。

関連項目:
詳細は、第18章「基本的なクエリー・リライト」を参照してください。

- リフレッシュの拡張
リフレッシュでは、UNION ALLマテリアライズド・ビューの自動索引作成、マテリアライズド・ビューのアトミック・リフレッシュ時のクエリー・リライトの使用、集合演算子を使用したマテリアライズド・ビューのリフレッシュをサポートするように拡張されました。また、UNION ALLマテリアライズド・ビューのパーティション・チェンジ・トラッキングのリフレッシュが可能になりました。さらに、カタログ・ビューは、パーティション化されたマテリアライズド・ビューの失効情報を含むように拡張されました。こうした改良により、リフレッシュ・パフォーマンスの高速化が期待できます。

関連項目:
詳細は、第16章「データ・ウェアハウスのメンテナンス」を参照してください。

- Oracle OLAPオプション・データ・ウェアハウス機能
Oracle DatabaseのOLAPオプションは、OLAPキューブを表に対する便利な代替手段として利用して、データ・ウェアハウスの集計データの管理および問合せを行うような機能がいくつか用意されました。次に、機能の詳細を示します。

- SQL問合せエンジンとキューブの新たな統合。1つは、Oracleクエリー・オプティマイザとキューブ行ソースへのキューブの統合です。こうした機能により、結合がOracle Databaseの多次元エンジンに直接プッシュされて表とキューブの間で効率的な結合が実行され、キューブからの選択時に秒当たりの総計行スループットが改善されることによって、OLAPキューブおよびOLAPディメンションから選択するSQL問合せの効率性が大幅に向上します。

- キューブで構成されたマテリアライズド・ビューに対する自動クエリー・リライト。キューブで構成されたマテリアライズド・ビューでは、表ではなくOLAPキューブからデータにアクセスします。アプリケーションは、表ベースのマテリアライズド・ビューと同様に、問合せをディテール表またはビューに書き込んだり、データベースにキューブ内の事前集計データへの問合せを自動的にリライトさせたりできます。

- データベース管理のキューブの自動リフレッシュ。このリリースでは、表ベースのマテリアライズド・ビューと同様にDBMS_MVIEW.REFRESHプログラムを使用してキューブをリフレッシュできます。キューブでは、FAST(増分)リフレッシュを強力にサポートしています。

- コストベースの集計。多くの状況で、キューブは表と比較して集計データの管理という点で非常に効率的です。コストベースの集計では、集計データの事前集計および問合せの効率の改善や、集計データの管理プロセスの簡素化によって、こうしたメリットがさらに高まります。

ビジネス・インテリジェンス・ツールやアプリケーションで問合せを行う、ディメンションとしてモデル化されたデータ・セット(スター・スキーマやスノーフレーク・スキーマなど)をサポートするデータベース管理者は、大幅なパフォーマンス・メリットを期待できるOLAPキューブをサマリー管理ソリューションとして使用することを検討する必要があります。

第I部

概念

この部では、データ・ウェアハウスの基本概念について説明します。

次の章で構成されています。

- 第1章「データ・ウェアハウスの概要」

1 データ・ウェアハウスの概要

この章では、Oracleのデータ・ウェアハウス実装の概要について説明します。内容は次のとおりです。

- データ・ウェアハウスの概要

- データ・ウェアハウス・アーキテクチャ

- データ・ウェアハウスからの情報の抽出

このマニュアルは、データ・ウェアハウスに関する標準テキストを補足するものです。Oracle固有の性能を中心に説明しており、データ・ウェアハウスの一般的な性能について詳細に説明するものではありません。標準テキストには、次の2冊があります。

- 『The Data Warehouse Toolkit』(Ralph Kimball著、John Wiley and Sons, 1996)

- 『Building the Data Warehouse』(William Inmon著、John Wiley and Sons, 1996)

データ・ウェアハウスの概要

データ・ウェアハウスとは、トランザクション処理ではなく、問合せおよび分析用に設計されたリレーショナル・データベースです。データ・ウェアハウスには、通常、トランザクション・データから導出された履歴データが含まれますが、他のソースからのデータを含めることもできます。データ・ウェアハウスは分析処理をトランザクション処理の負荷と分離し、組織で、様々なソースからのデータを整理統合できるようにします。これは、次のような場合に役立ちます。

- 履歴データのメンテナンス

- ビジネスをよりよく理解し改善するためのデータの分析

データ・ウェアハウス環境には、リレーショナル・データベースに加えて、抽出、転送、変換、ロード(ETL)のソリューションや、統計分析、レポート、データ・マイニング機能、クライアント分析ツール、および、データ収集処理や有益かつ実用的な情報へのデータ変換処理、ビジネス・ユーザーへのデータ配信処理を管理するその他のアプリケーションが含まれます。

参照:
第12章「抽出、変換、ロードの概要」

データ・ウェアハウスを導入する際は、William Inmon氏が提唱するデータ・ウェアハウスの次の特性を十分に理解する必要があります。

- サブジェクト指向

- 統合化

- 恒常的

- 時系列

サブジェクト指向

データ・ウェアハウスは、データの分析に役立つように設計されています。たとえば、会社の売上データの詳細が必要な場合は、売上を中心とするデータ・ウェアハウスを作成できます。このデータ・ウェアハウスでは、「昨年、この品目を最も多く購入した顧客は誰だったか」、「来年、この品目を最も多く購入しそうな顧客は誰か」のような質問に答えることができます。このようにデータ・ウェアハウスをサブジェクト(この場合は売上)別に定義できるため、データ・ウェアハウスの使用はサブジェクト指向となります。

統合化

統合化は、サブジェクト指向と密接な関係があります。データ・ウェアハウスでは、異なるソースのデータを、一貫したフォーマットに入れる必要があります。また、ネーミングの競合や単位の不整合などの問題を解決する必要があります。これが達成されれば、データ・ウェアハウスは統合化されたことになります。

恒常的

恒常的とは、一度データ・ウェアハウスに入ったデータは変更できないことを意味します。これは、データ・ウェアハウスの目的が、何が発生したかを分析することにあるためです。

時系列

データ・ウェアハウスでは、時系列という用語が意味する、時間経過に伴う変化に重点が置かれています。ビジネスの動向を見いだし、背後に潜むパターンおよび関係性を識別するには、アナリストは大量のデータを必要とします。これは、オンライン・トランザクション処理(OLTP)システムとは非常に対照的です。OLTPシステムでは、パフォーマンス要件のために、履歴データをアーカイブに移動させる必要があります。

OLTPとデータ・ウェアハウス環境の比較

図1-1に、OLTPシステムとデータ・ウェアハウスの主な違いを示します。

図1-1 OLTPとデータ・ウェアハウス環境の比較

この2つのシステムにおける大きな違いの1つは、OLTP環境での一般的なデータ正規化タイプは第3正規形(3NF)ですが、データ・ウェアハウスでは、通常そうではないということです。

データ・ウェアハウスおよびOLTPシステムの要件は、大きく異なります。次に、典型的なデータ・ウェアハウスとOLTPシステムのいくつかの違いの例を示します。

- 処理負荷
データ・ウェアハウスは、非定型の問合せおよびデータ分析に適応するように設計されています。データ・ウェアハウスの処理負荷は、事前には不明な場合があります。このため、データ・ウェアハウスは、様々な問合せ操作および分析操作を適切に実行できるように最適化する必要があります。

OLTPシステムは、事前に定義された操作のみサポートします。アプリケーションは、これらの操作のみサポートするようにチューニングまたは設計されている場合があります。

- データ修正
データ・ウェアハウスのデータは、大量データ修正の技術を使用して、ETLプロセスによって定期的に(毎晩、毎週など)更新されます。データ・マイニングなどの分析ツールを使用して、関連する確率から予測を作成したり、顧客を市場セグメントに当てはめたり、顧客プロファイルを作成したりする場合を除き、データ・ウェアハウスのエンド・ユーザーは、データ・ウェアハウスを直接更新することはありません。

OLTPシステムでは、エンド・ユーザーが機械的に、個々のデータ修正の都度、修正文を発行します。OLTPデータベースは常に最新であり、各ビジネス・トランザクションの現在の状態が反映されます。

- スキーマ設計
データ・ウェアハウスは、非正規化または部分的に非正規化されたスキーマ(スター・スキーマなど)を使用して、問合せおよび分析のパフォーマンスを最適化します。

OLTPシステムは、完全に正規化されたスキーマを使用して、更新/挿入/削除のパフォーマンスを最適化し、データ整合性を保証します。

- 典型的な操作
典型的なデータ・ウェアハウスの問合せでは、膨大な数の列がスキャンされる場合があります。たとえば、「先月のすべての顧客に対する合計売上の検索」などの場合です。

典型的なOLTP操作では、少数のレコードのみがアクセスされます。たとえば、「この顧客に対する現在の注文の取出し」などの場合です。

- 履歴データ
データ・ウェアハウスには、通常、長い年月分のデータが格納されています。これは、履歴の分析およびレポートをサポートするためです。

OLTPシステムには、通常、数週間または数か月分のデータのみが格納されています。OLTPシステムには、現行のトランザクション要件を満たすために必要な履歴データのみが格納されます。

データ・ウェアハウス・アーキテクチャ

データ・ウェアハウスおよびそのアーキテクチャは、組織の特定の状況に応じて変化します。一般的なアーキテクチャは、次の3つです。

- データ・ウェアハウス・アーキテクチャ: 基本

- データ・ウェアハウス・アーキテクチャ: ステージング・エリアを伴う

- データ・ウェアハウス・アーキテクチャ: ステージング・エリアおよびデータ・マートを伴う

データ・ウェアハウス・アーキテクチャ: 基本

図1-2に、データ・ウェアハウスの単純なアーキテクチャを示します。エンド・ユーザーは、複数のソース・システムから導出されたデータに、データ・ウェアハウスを通じて直接アクセスします。

図1-2 データ・ウェアハウスのアーキテクチャ

図1-2では、従来のOLTPシステムのメタデータと生データは、付加的なデータであるサマリー・データと同様に示されています。長時間かかる作業を事前計算するサマリーは、データ・ウェアハウスでは大変役立ちます。たとえば、典型的なデータ・ウェアハウス問合せとして、8月の売上などを取り出す問合せがあります。Oracleデータベースでは、サマリーをマテリアライズド・ビューと呼びます。

データ・ウェアハウス・アーキテクチャ: ステージング・エリアを伴う

図1-3に示すように、業務系データはウェアハウスに格納する前にクレンジングして加工しておく必要があります。これはプログラムで処理できますが、ほとんどのデータ・ウェアハウスではステージング・エリアが使用されます。ステージング・エリアにより、サマリーの作成とウェアハウス管理全般が簡素化されます。図1-3に、この典型的なアーキテクチャを示します。

図1-3 データ・ウェアハウス・アーキテクチャ(ステージング・エリアを伴う)

データ・ウェアハウス・アーキテクチャ: ステージング・エリアおよびデータ・マートを伴う

図1-3のアーキテクチャはきわめて一般的ですが、ウェアハウスのアーキテクチャを組織内のグループごとにカスタマイズすることもできます。そのためには、特定の業務に合せて設計されたデータ・マートを追加します。図1-4の例では、発注、受注、在庫が分離されています。この例で、ファイナンシャル・アナリストは発注と受注の履歴データを分析したり、履歴データをマイニングして顧客の行動の予測を作成できます。

図1-4 データ・ウェアハウス・アーキテクチャ(ステージング・エリアおよびデータ・マートを伴う)

注意:
データ・マートは多くのデータ・ウェアハウスにおいて重要ですが、このマニュアルでは重点を置いていません。

データ・ウェアハウスからの情報の抽出

データを分析することにより、データ・ウェアハウスに格納された大量のデータから情報を抽出できます。Oracle Databaseでは、いくつかの方法でデータを分析できます。

- 記述統計、仮説の検証、相関分析、分布の適合度の検証、カイ2乗統計によるクロス集計、および分散分析(ANOVA)などの、多様な統計関数(これらの関数については、『Oracle Database SQL言語リファレンス』を参照)

- OLAP

- データ・マイニング

OLAP

Oracle Databaseは、業界初で唯一の埋込みOLAPサーバーを提供します。Oracle OLAPにより、複数のディメンションにまたがってデータを分析する際に、固有の多次元格納および高速な応答時間を実現できます。データベースでは、時系列計算、予測、加算および非加算の演算子や割当て演算子による高度な集計など、多様な形で分析を行うことができます。こうした機能により、Oracleデータベースは、ビジネス・インテリジェンスや高度な分析アプリケーション全般をサポートできる、完全な分析プラットフォームとなります。

Oracle OLAPでは、多次元データ・モデルを使用して、リアルタイムで履歴データの複雑な統計分析、算術分析および財務分析を行います。Oracle OLAPはデータベースに完全に統合されているため、標準のSQL管理ツール、問合せツールおよびレポート・ツールを使用できます。

OLAPの詳細は、『Oracle OLAPユーザーズ・ガイド』を参照してください。

多次元テクノロジの完全統合

Oracleでは、多次元オブジェクトおよび分析をデータベースに統合することにより、両者の利点を活かし、Oracleデータベースの信頼性、可用性、セキュリティ、スケーラビリティとともに多次元分析機能を実現しています。

Oracle OLAPはOracle Databaseに完全に統合されています。これは、技術レベルでは次のことを意味します。

- OLAPエンジンは、Oracle Databaseのカーネルで実行されます。

- ディメンション・オブジェクトは、固有の多次元形式でOracle Databaseに格納されます。

- キューブなどのディメンション・オブジェクトは、Oracleデータ・ディクショナリの主要なデータ・オブジェクトです。

- データ・セキュリティは、Oracle Databaseのユーザーとロールに対して権限の付与および取消しを行う、標準的な方法で管理されます。

- アプリケーションで、SQLを使用してディメンション・オブジェクトを問い合せることができます。

これは組織にとって非常に有益です。Oracle OLAPは、簡素化によるメリットをもたらします。つまり、1つのデータベース、標準の管理およびセキュリティ機能、標準のインタフェースや開発ツールを利用できるのです。

アプリケーション開発の容易性

Oracle OLAPでは、特徴的な分析コンテンツでデータベースやアプリケーションを簡単に強化できます。Oracleの多次元オブジェクトや計算に対する固有のSQLアクセスによって、ダッシュボード、レポート、ビジネス・インテリジェンス(BI)、あらゆるタイプの分析アプリケーションの開発作業が、独自のインタフェースを提供するシステムと比較して非常に容易になります。また、SQLアクセスは、従来の限られたOLAPアプリケーションのみではなく、どのようなデータベース・アプリケーションでも、Oracle OLAPの分析機能が使用できることを意味します。

管理の容易性

Oracle OLAPはOracleデータベースに完全に埋め込まれているため、通常スタンドアロンのOLAPサーバーに必須となる管理技術を習得する必要がありません。専門の管理スキルに投資することなく、既存のDBAスタッフを活用できます。

Oracleの埋込みOLAPテクノロジの主な管理上のメリットの1つは、キューブの自動的なメンテナンスです。スタンドアロンのOLAPサーバーでは、キューブのリフレッシュ作業の負担がすべて管理者にかかります。その結果、ジョブが複雑になり、エラーが発生しやすくなることがあります。管理者は、リレーショナル・ソースから変更済データを抽出し、そのデータをソース・システムからスタンドアロンOLAPサーバーを実行しているシステムに移動し、キューブのロードと再構築を行うプロシージャを作成する必要があります。また、このプロセスの間、DBAはデルタ(変更された値)のセキュリティについて責任を負わなければなりません。

その一方、Oracle OLAPでは、キューブのリフレッシュはすべてOracleデータベースで処理されます。データベースは、ディメンション・オブジェクトの失効を追跡し、ソース表のデルタの動きを自動的に記録して、リフレッシュ・プロセス中に変更された値のみを自動的に適用します。DBAはリフレッシュのスケジュールを適切な間隔で設定するだけで、他のことについてはすべてOracle Databaseが対処します。

セキュリティ

Oracle OLAPでは、Oracle Databaseの標準のセキュリティ機能を使用して、多次元データを保護します。

これに対し、スタンドアロンのOLAPサーバーでは、管理者はリレーショナル・ソース・システムとOLAPサーバー・システムで、2回に渡ってセキュリティの管理を行う必要があります。また、リレーショナル・システムからスタンドアロンOLAPシステムにデータを移動する際のセキュリティを管理しなければなりません。

優れたパフォーマンスとスケーラビリティ

ビジネス・インテリジェンスおよび分析アプリケーションは主に、階層のドリルアップやドリルダウン、集計値の比較(前期比、親のシェア、将来の予測、多数の類似の計算など)といった操作に使用します。こうした操作は多くの場合、階層集計が可能な領域全体においてランダムに行われます。Oracle OLAPは、定義された多次元領域ですべての集計を事前計算するか、効率よく即時計算するため、典型的なビジネス・インテリジェンス・アプリケーションで優れたパフォーマンスを実現できます。

Oracle OLAPの問合せでは、Oracleの共有カーソルを利用することにより、メモリー要件を大幅に削減し、パフォーマンスを向上させます。

Oracle DatabaseとともにReal Application Clusters(Oracle RAC)オプションをインストールすると、OLAPアプリケーションにおいても、パフォーマンス、スケーラビリティ、フェイルオーバーおよびロード・バランシングについて、他のアプリケーションと同様の利点があります。

コストの削減

上述の機能はすべて、コストの削減につながります。既存のスタッフのスキルを利用できるため、管理費が減少します。また、Oracleデータベースでは、他のシステムにおいては管理者の負担となる複雑な作業である、ディメンション・オブジェクトのリフレッシュの管理が可能です。標準のセキュリティによっても、管理費が削減されます。SQLに精通した多数のアプリケーション開発者や豊富なSQLベースの開発ツールを活用できるため、アプリケーションの開発とデプロイを迅速に行うことができ、アプリケーション開発費が減少します。いずれのSQLベースの開発ツールでもOracle OLAPを利用できます。Oracle OLAPの効率的な集計の管理、共有カーソルの使用、低コストのコモディティ部品でスケーラブルなシステムを構築できるOracle RACによって、ハードウェア費が削減されます。

ディメンション・オブジェクトの問合せ

Oracle OLAPでは多数の分析コンテンツが用意されており、問合せに対する応答も高速なため、お使いのSQLアプリケーションを強化できます。SQL問合せのインタフェースにより、OLAPの知識がなくても、すべてのアプリケーションでキューブやディメンションへの問合せを実行できます。

OLAPオプションでは、キューブ、ディメンションおよび階層のリレーショナル・ビューを自動的に生成します。SQLアプリケーションでこれらのビューを問い合せることにより、分析担当者や意思決定者はこれらのオブジェクトの情報量に富んだ内容を表示できます。また、実表などのシステム生成ビューを使用して、お使いのアプリケーションが求める構造に適合するカスタム・ビューを作成できます。

分析者は、SQL問合せと分析ツールを選択して、データの選択、表示および分析を行うことができます。好みのツールやアプリケーション、またはOracle Databaseのツール(Oracle Application ExpressやBusiness Intelligence Publisherなど)を使用できます。

サマリー・データの効率的な格納および均一な可用性

キューブ・マテリアライズド・ビューでは、詳細なリレーショナル表への問合せを行うアプリケーション、およびキューブに直接問合せを行うアプリケーションに対して、OLAPオプションの高速更新および高速問合せの機能が提供されます。

ファクト表に含まれるサマリーの多数のリレーショナル・マテリアライズド・ビューは、単一のキューブ・マテリアライズド・ビューに置き換えることができます。そのため、クエリー・リライトの間の全サマリー・データへの応答時間は均一になります。アプリケーションでは、優れた問合せパフォーマンスが得られます。

キューブ・マテリアライズド・ビューは、Oracle Databaseの自動リフレッシュ機能およびクエリー・リライト機能のために拡張されたキューブです。サマリー・データが生成され、キューブに格納されると、クエリー・リライト機能によって問合せがキューブ・マテリアライズド・ビューに自動的にリダイレクトされます。

リレーショナル・マテリアライズド・ビューをサポートするディクショナリ・ビューおよびPL/SQLパッケージでは、多くの場合、キューブ・マテリアライズド・ビューもサポートされます。さらに、DBMS_CUBEでPL/SQLサブプログラム・グループを使用すると、既存のリレーショナル・マテリアライズド・ビューからキューブ・マテリアライズド・ビューを迅速にデプロイできます。

ディメンション・オブジェクトの作成および管理のためのツール

Analytic Workspace Managerは、Oracle Database内のディメンション・オブジェクトを作成、構築および管理するためのメイン・ツールです。

Oracle OLAPはデータベース内にあり、そのリソースは同じツール(Oracle Enterprise Manager Database Control、自動ワークロード・リポジトリ、自動データベース診断モニターなど)を使用して管理されます。

データ・マイニング

データ・マイニングでは、大量のデータを使用してモデルを作成します。こうしたモデルにより、データに埋もれた重要かつ貴重な情報を見つけ出すことができます。たとえば、データ・マイニングを使用すると次のことが可能になります。

- サービス業者を変更する見込みが高い顧客を予測する

- 病気に関係のある要素を発見する

- 不正行為を識別する

データ・マイニングにより、ビジネスの様々な問題が解決されます。たとえば、データ・マイニングを使用して、減少する可能性がある顧客層を予測できます。

Oracle Data Miningは、データ・マイニングをOracle Database内で実行します。Oracle Data Miningでは、データベースと外部マイニング・サーバー間でのデータ移動を必要としません。このことは、冗長性の排除、データの格納と処理の効率性向上、使用するデータの最新状態の維持、およびデータ・セキュリティの確保につながります。

Oracle Data Miningの詳細は、『Oracle Data Mining概要』を参照してください。

Oracle Data Miningの機能

Oracle Data Miningは、主なデータ・マイニング機能をサポートしています。各マイニング機能には、少なくとも1つのアルゴリズムがあります。

Oracle Data Miningは、次のデータ・マイニング機能をサポートしています。

- 分類: 項目を個別のクラスにグループ化し、ある項目がどのクラスに属するかを予測します。分類に使用されるアルゴリズムは、Decision Tree、Naive Bayes、一般化線型モデル(2項ロジスティック回帰)、およびサポート・ベクター・マシンです。

- 回帰: 連続する数値の近似および予測を行います。回帰に使用されるアルゴリズムは、サポート・ベクター・マシンおよび一般化線型モデル(多変量線型回帰)です。

- 異常検出: 不正および侵入などの異常ケースを検出します。異常検出に使用されるアルゴリズムは、1クラス・サポート・ベクター・マシンです。

- 属性評価: ターゲット属性(たとえば、解約の可能性が高い顧客)と最も強い関係を持つ属性を識別します。属性評価に使用されるアルゴリズムは、最小記述長です。

- クラスタリング: 顧客セグメントの識別に多く使用される、データ内の自然なグループを見つけます。クラスタリングに使用されるアルゴリズムは、k-MeansおよびO-Clusterです。

- 相関: 「マーケット・バスケット」、つまり同時に購入される可能性が高い商品群を分析します。相関に使用されるアルゴリズムは、a prioriです。

- 特徴抽出: 新しい属性(特徴)を元の属性の組合せとして作成します。特徴抽出に使用されるアルゴリズムは、Non-Negative Matrix Factorizationです。

Oracle Data Miningでは、構造化データのマイニングのみでなく、テキスト・データ(警察の報告や顧客のコメント、医師のメモなど)や空間データのマイニングも行えます。

Oracle Data Miningのインタフェース

Oracle Data MiningのAPIは、データ・マイニング結果の抽出および配布を自動化するアプリケーションの開発に関して、次のような幅広いサポートを提供しています。

モデルの作成、テストおよびスコアリングなどのデータ・マイニング・アクティビティは、PL/SQL API、Java APIおよびSQLデータ・マイニング関数を使用して実行できます。Java APIはデータ・マイニング規格のJSR 73に準拠しています。Java APIとPL/SQL APIの間には完全な相互運用性があります。

Oracle Data Miningでは、スーパーモデル、つまりデータ準備の手順を含むモデルを作成できます。埋め込まれたデータ準備は、自動と手動のどちらでも実装できます。埋込みデータ準備は、ユーザー指定のデータ変換をサポートします。自動データ準備は、アルゴリズムが必要とするデータ準備(ビニング、正規化、外れ値の処理など)をサポートします。

SQLデータ・マイニング関数は、分類モデル、回帰モデル、クラスタリング・モデル、および特徴抽出モデルのスコアリングをサポートしています。他のSQL問合せと同様、標準SQL文のコンテキスト内で、事前に作成したモデルを新しいデータや後続処理用に戻された結果へ適用できます。

予測分析により、データ・マイニングの処理を自動化します。ユーザーの介入なしに、予測分析ルーチンがデータ準備、アルゴリズムの選択、モデルの作成、およびモデルのスコアリングを管理するため、データ・マイニングの専門家でなくともデータ・マイニングを活用できます。

Oracle Data Minerは、Oracle Data Miningのグラフィカル・ユーザー・インタフェースです。Oracle Data Minerの画面に従って、データ準備、データ・マイニング、モデル評価およびモデル・スコアリングのすべての手順を実行できます。Oracle Data Miningインタフェースの詳細は、『Oracle Data Mining概要』を参照してください。

第II部

論理設計

この部では、データ・ウェアハウスにおける論理設計の問題について説明します。

次の章で構成されています。

- 第2章「データ・ウェアハウスの論理設計」

2 データ・ウェアハウスの論理設計

この章では、データ・ウェアハウス環境の論理設計の作成方法について説明します。内容は次のとおりです。

- データ・ウェアハウスの論理設計と物理設計の比較

- 論理設計の作成

- データ・ウェアハウス・スキーマ

- データ・ウェアハウス・オブジェクト

データ・ウェアハウスの論理設計と物理設計の比較

組織でのデータ・ウェアハウス構築がすでに決定されているとします。そして、ビジネス要件の定義、アプリケーションの適用範囲の打合せ、および概念的な設計も完了しているとします。そこで、その要件をシステムに移行できるように変換する必要があります。そのためには、データ・ウェアハウスの論理設計と物理設計を行います。定義する内容は、次のとおりです。

- 具体的なデータ内容

- データ・グループ内およびデータ・グループ間の関係

- データ・ウェアハウスをサポートするシステム環境

- 必要なデータ変換

- データのリフレッシュ頻度

論理設計は、物理設計に比べて概念的で抽象的です。論理設計では、オブジェクト間の論理的な関係を検討します。物理設計では、オブジェクトの格納と取出しの他、転送処理およびバックアップ/リカバリの観点から、最も効果的な方法を検討します。

設計では、エンド・ユーザーのニーズを優先させる必要があります。エンド・ユーザーは、通常、個々のトランザクションではなく、分析を行って集計されたデータを見ます。ただし、実際にデータを見るまで、何が必要であるかがエンド・ユーザーにはわからない場合があります。適切に計画された設計であれば、ユーザーのニーズの変化や発展に応じて、拡張および変更を行うことができます。

まず、論理設計から始めると、情報についての要件に集中でき、実装の詳細は後で検討できます。

論理設計の作成

論理設計とは、概念的で抽象的な設計です。ここでは、物理的な実装の詳細は説明しません。必要な情報の種類の定義方法のみを扱います。

組織の論理的な情報要件のモデリングに使用できるテクニックの1つが、E-Rモデリングです。E-Rモデリングでは、重要事項(エンティティ)、重要事項のプロパティ(属性)および相互の関連(リレーションシップ)を特定します。

物理設計プロセスでは、データを、エンティティおよび属性と呼ばれる、一連の論理的な関係に配置します。エンティティは、情報の大きいまとまりを表します。エンティティは、リレーショナル・データベースの表にマップされます。属性はエンティティのコンポーネントで、エンティティの一意性を定義します。属性は、リレーショナル・データベースの列にマップされます。

データの一貫性を確実にするには、一意識別子を使用する必要があります。一意識別子は、同じ項目が異なる場所に表示される場合に、両者を区別できるように表に追加する識別子です。これは通常、物理設計における主キーです。

従来、E-RダイアグラムはOLTPアプリケーションなど高度に正規化されたモデルに関連付けられてきましたが、そのテクニックは、ディメンショナル・モデリングにも役立ちます。ディメンショナル・モデリングでは、情報の基本単位(エンティティや属性など)およびそれらのすべてのリレーションシップを明確にするのではなく、情報を、中心となるファクト表の情報と、関係するディメンション表の情報とに識別します。ビジネス・サブジェクトかデータ・フィールドかの識別、ビジネス・サブジェクト間のリレーションシップの定義、および各サブジェクトに対する属性のネーミングを行います。

参照:
ディメンションの詳細は、第11章「ディメンション」を参照してください。

論理設計で作成する必要があるのは、(1)ファクト表とディメンション表に対応するエンティティと属性のセット、および(2)ソースの業務系データを、ターゲットとなるデータ・ウェアハウス・スキーマ内の、サブジェクト指向情報に入れるまでのモデルです。

論理設計は、ペンと紙を使用するか、またはOracle Warehouse Builder(ETLプロセスのモデリング・サポート用に特別に設計されたツール)などの設計ツールを使用して作成できます。

関連項目:
Oracle Warehouse Builderのドキュメント・セット

データ・ウェアハウス・スキーマ

スキーマとは、表、ビュー、索引およびシノニムを含むデータベース・オブジェクトのコレクションです。データ・ウェアハウス用に設計されたスキーマ・モデルには、様々な方法でスキーマ・オブジェクトを配置できます。ほとんどのデータ・ウェアハウスでは、ディメンショナル・モデルが使用されます。

データ・ウェアハウス・スキーマの設計には、ソース・データのモデルとユーザー要件を使用できます。会社のエンタープライズ・データ・モデルからソース・データを取得し、これを基にデータ・ウェアハウス用の論理データ・モデルをリバース・エンジニアすることが可能な場合があります。論理データ・ウェアハウス・モデルの物理的な実装には、コンピュータのサイズ、ユーザー数、記憶域の容量、ネットワークの種類、ソフトウェアなどのシステム・パラメータに応じて、多少の変更が必要な場合があります。

スター・スキーマ

スター・スキーマは、最も単純なデータ・ウェアハウス・スキーマです。スター・スキーマを図で表すと、星(スター)のように中央から放射状に広がっているため、スター・スキーマと呼ばれます。図2-1に示すように、スターの中心には1つ以上のファクト表があり、スターの先端にはディメンション表があります。

図2-1 スター・スキーマ

データ・ウェアハウス・モデルを作成する最も自然な方法はスター・スキーマです。スター・スキーマでは、ファクト表と1つのディメンション表とのリレーションシップを確立するために必要な結合は1つのみです。

スター・スキーマでは、問合せを単純化し、応答時間を短縮することでパフォーマンスが最適化されます。各レベルに関するすべての情報は、1つの行に格納されます。

その他のデータ・ウェアハウス・スキーマ

データ・ウェアハウス環境の一部のスキーマでは、スター・スキーマではなく第3正規形が使用されます。また、スノーフレーク・スキーマが有効なスキーマもあります。スノーフレーク・スキーマは、ディメンションがツリー構造で正規化されているスター・スキーマです。さらに、OLAPのスキーマも使用できます。OLAPスキーマは、Oracle Database内のキューブやディメンションなどの次元データ型をサポートします。

関連項目:
データ・ウェアハウスでのスター・スキーマとスノーフレーク・スキーマの詳細は、第20章「スキーマのモデリング化技法」、概念情報の詳細は、『Oracle Database概要』、OLAPスキーマの詳細は『Oracle OLAPユーザーズ・ガイド』を参照してください。

データ・ウェアハウス・オブジェクト

ファクト表とディメンション表は、データ・ウェアハウスのディメンション・スキーマで一般的に使用される2種類のオブジェクトです。

ファクト表は、ビジネス関連の測定データが格納されるデータ・ウェアハウス・スキーマ内の大規模な表です。通常、ファクト表には、ディメンション表への外部キーおよびファクトが含まれます。また、ファクト表は、分析および調査のできる数値データおよび加算データを表します。たとえば、売上、コスト、利益などです。

ディメンション表(参照表とも呼ぶ)には、データ・ウェアハウスの比較的静的なデータが含まれます。たとえば、問合せを含めるために通常使用する情報が格納されます。通常、ディメンション表は説明的なテキストであり、結果セットの行ヘッダーとして使用できます。たとえば、顧客、製品などです。

データ・ウェアハウス・オブジェクト: ファクト表

ファクト表には、通常、数値ファクト(メジャーとも呼ぶ)を含む列と、ディメンション表への外部キーとなる列の2種類の列があります。ファクト表には、ディテール・レベルのファクトまたは集計ファクトのいずれかが含まれます。集計ファクトを含むファクト表は、一般にサマリー表と呼ばれます。通常、ファクト表には同じ集計レベルのファクトが含まれています。ほとんどのファクトは加算的ですが、準加算的なものや非加算的なものもあります。加算ファクトは、単純な加算で集計できます。その最も一般的な例が売上です。非加算ファクトは加算できません。その一例が平均です。準加算ファクトは、ディメンションの一部については集計できますが、他の部分については集計できません。その一例が在庫レベルで、レベルが何を意味するかは単に見ただけではわかりません。

ファクト表の要件

各スター・スキーマに対してファクト表を定義する必要があります。モデリングの観点からすると、ファクト表の主キーは、通常、すべての外部キーで構成されるコンポジット・キーです。

データ・ウェアハウス・オブジェクト: ディメンション表

ディメンションは、データを分類する1つの構造であり、1つ以上の階層から構成されています。ディメンション属性を使用して、ディメンション値を記述できます。通常、ディメンションは説明的なテキスト値です。ファクトと結合した複数の独立したディメンションにより、ビジネス上の問題に対応できます。最も一般的なディメンションは、顧客(customers)、製品(products)および時間(times)です。

ディメンション・データは、通常、詳細な最下位レベルで収集されてから、分析に有効な上位レベルの合計に集計されます。このようなディメンション表内でのロールアップまたは集計を階層と呼びます。

階層

階層は、データを構成する順序付けられたレベルを使用する論理構造です。階層は、データ集計の定義に使用できます。たとえば、時間ディメンションであれば、月レベルから四半期レベル、さらに年レベルへとデータを集計する階層を定義できます。階層は、ナビゲーショナル・ドリル・パスの定義およびファミリ構造の作成にも使用できます。

階層内では、各レベルがその上下のレベルと論理的に接続されます。下位レベルのデータ値は、上位レベルのデータ値に集計されます。ディメンションは複数の階層で構成できます。たとえば、製品ディメンションには、2つの階層(製品カテゴリおよび製品仕入先)がある場合があります。

また、ディメンション階層は、要約レベルから詳細レベルまでカテゴリ化されます。階層は、問合せツールによって使用されます。これにより、データをドリルダウンし、詳細度の異なるレベルを参照できるようになります。これは、データ・ウェアハウスの主なメリットの1つです。

階層を設計する場合は、ビジネス構造におけるリレーションシップを考慮する必要があります。たとえば、部門別の複数レベルの営業組織などが考えられます。

階層では、ディメンション値はファミリ構造で表現されます。あるレベルの値に対して、1つ上のレベルの値はその親になり、1つ下のレベルの値はその子になります。これらのファミリ関係によって、アナリストはデータに迅速にアクセスできます。

レベル

レベルは、階層における1つの位置を表します。たとえば、時間ディメンションには、月、四半期および年レベルのデータを表す階層があります。レベルには、要約レベルから詳細レベルまであり、ルート・レベルは最上位レベルで、最も要約の進んだレベルです。ディメンション内のレベルは、1つ以上の階層に編成されます。

レベル間の関係

レベル間の関係によって、最も要約された情報(ルート)から最も限定的な情報まで、上位レベルから下位レベルまでの順序付けが指定されます。階層内のレベル間の親子関係が定義されます。

階層は、より複雑なリライトを使用可能にするためにも不可欠なコンポーネントです。たとえば、データベースでは、四半期と年とのディメンションの依存性がわかっていれば、既存の販売収入を四半期別から年別へと集計できます。

典型的なディメンション階層

図2-2に、顧客をベースとするディメンション階層を示します。

図2-2 一般的なディメンションの階層レベル

関連項目:
階層の詳細は、第11章「ディメンション」および第18章「基本的なクエリー・リライト」を参照してください。

データ・ウェアハウス・オブジェクト: 一意識別子

一意識別子は、ディメンション表の値が一意なレコードに対して指定されます。一意識別子が指定されたデータの変更による問題を回避するために、通常は人為的に作成された一意識別子が使用されます。一意識別子は、#文字で表されます。たとえば、#customer_idとなります。

データ・ウェアハウス・オブジェクト: リレーションシップ

リレーションシップにより、ビジネスの整合性が保証されます。たとえば、販売会社は必ず顧客と製品を持っています。ファクト表とディメンション表の製品および顧客について売上情報のリレーションシップを設計することで、データベース内でビジネス・ルールが規定されます。

データ・ウェアハウスのオブジェクトとそのリレーションシップの例

図2-3に、salesファクト表とディメンション表customers、products、promotions、timesおよびchannelsの一般的な例を示します。

図2-3 一般的なデータ・ウェアハウス・オブジェクト

第III部

物理設計

この部では、データ・ウェアハウスの物理設計について説明します。

次の章で構成されています。

- 第3章「データ・ウェアハウスの物理設計」

- 第4章「データ・ウェアハウスにおけるハードウェアおよびI/Oの考慮事項」

- 第5章「データ・ウェアハウスにおけるパーティション化」

- 第6章「データ・ウェアハウスにおけるパラレル実行」

- 第7章「索引」

- 第8章「整合性制約」

- 第9章「基本的なマテリアライズド・ビュー」

- 第10章「高度なマテリアライズド・ビュー」

- 第11章「ディメンション」

3 データ・ウェアハウスの物理設計

この章では、データ・ウェアハウス環境の物理設計について説明します。内容は次のとおりです。

- 論理設計から物理設計への変換

- 物理設計

論理設計から物理設計への変換

論理設計とは、データ・ウェアハウスを作成する前に、設計を紙に書いたり、Oracle Warehouse BuilderまたはOracle Designerで設計したりすることです。物理設計とは、SQL文でデータベースを作成することです。

物理設計では、論理設計時に収集したデータを、物理データベース構造の記述に変換します。物理設計上の決定事項には、主に問合せのパフォーマンスやデータベースのメンテナンスが影響します。たとえば、問合せ要件に適したパーティション化を行うと、実行前に検索対象を絞り込むパーティション・プルーニングをOracle Databaseで活用できます。

参照:

- パーティション化の詳細は、第5章「データ・ウェアハウスにおけるパーティション化」を参照してください。

- すべての設計事項の概念情報の詳細は、『Oracle Database概要』を参照してください。

物理設計

論理設計の段階で、エンティティ、属性およびリレーションシップで構成されるデータ・ウェアハウスのモデルが定義されています。エンティティは、リレーションシップを使用して相互にリンクされます。属性は、エンティティの説明に使用します。一意識別子(UID)により、エンティティの1つのインスタンスとその他のインスタンスが区別されます。

図3-1に論理設計と物理設計の違いを示します。

図3-1 論理設計と物理設計の比較

物理設計では、必要となるスキーマを実際のデータベース構造に変換します。ここで、次のマッピングを行う必要があります。

- エンティティから表へ

- リレーションシップから外部キー制約へ

- 属性から列へ

- 1次一意識別子から主キー制約へ

- 一意識別子から一意キー制約へ

物理設計の構造

論理設計を物理設計に変換した後に、次の一部またはすべての構造を作成する必要があります。

- 表領域

- 表とパーティション表

- ビュー

- 整合性制約

- ディメンション

これらの構造の中には、ディスク領域を必要とするものがあります。データ・ディクショナリにのみ存在するものもあります。また、パフォーマンス改善のために次の構造を作成できます。

- 索引とパーティション索引

- マテリアライズド・ビュー

表領域

表領域は、使用中のオペレーティング・システム内の物理構造である1つ以上のデータファイルで構成されます。データファイルは、1つの表領域にのみ対応付けられています。設計の観点からは、表領域は物理設計構造のコンテナです。

表領域は、相違点によって分離する必要があります。たとえば、表と索引、小規模な表と大規模な表は分離する必要があります。また、表領域は、可能であれば論理的なビジネス・ユニットを表す必要があります。表領域は、バックアップやリカバリ、またはトランスポータブル表領域メカニズムのための最も大きな単位であるため、論理的なビジネス設計は可用性とメンテナンス操作に影響します。

現在では、巨大なデータファイルを使用でき、非常に大きなデータベースでのパフォーマンスが大幅に改善されています。

関連項目:
表領域については、第4章「データ・ウェアハウスにおけるハードウェアおよびI/Oの考慮事項」を参照してください。

表とパーティション表

表は、基本的なデータ格納単位です。これらは、データ・ウェアハウス内で予測された量の生データのコンテナです。

非パーティション表のかわりにパーティション表を使用し、より小さく管理しやすく分割できるようにすることにより、大量のデータをサポートする際の主要な問題に対処できます。パーティション化の主な設計基準は管理のしやすさですが、ほとんどの場合、パーティション・プルーニングやインテリジェントなパラレル処理により、パフォーマンスも向上します。たとえば、受注取引日に基づいて月毎にパーティション化できます。4年分のデータがある場合も、単一の高速なDDL文を使用して4年以上経過したデータを月単位で順次削除し、新しいデータをロードできます。このとき影響が及ぶのは表全体の1/48のみです。最終の四半期に関するビジネスの問合せが影響するのはたった3か月分(3パーティション分)、つまり全体の3/48となります。

大規模な表をパーティション化すると、各パーティションの管理がさらに簡単になるため、パフォーマンスが向上します。通常、データ・ウェアハウス内のトランザクションの日付(月ごとなど)に基づいてパーティション化を行います。たとえば、毎月、1か月分のデータを独自のパーティションに割り当てることができます。

表の圧縮

ヒープ構成表を圧縮すると、ディスク領域の節約、メモリー効率の向上および問合せパフォーマンスの改善が可能になります。また、スケーラビリティおよび問合せパフォーマンスの向上につながります。圧縮は、表領域、表またはパーティション・レベルで有効にできます。表の圧縮を考慮する必要がある典型的なヒープ構成表タイプは、パーティション表です。圧縮されている表やパーティションは更新可能ですが、このような表の更新にはオーバーヘッドが発生し、頻繁な更新アクティビティで領域が浪費されて圧縮の妨げになる場合があります。

更新アクティビティが多い表には、OLTP表圧縮が最適です。特定のOracleストレージ・システムの機能であるハイブリッド・コラム圧縮では、データの格納に行と列の両方の方法を組み合せて使用します。データがロードされると、行のグループは列形式で格納され、指定した列の値が一緒に格納されて圧縮されます。同じデータ型や同様の特性の列データを一緒に格納すると、圧縮によってストレージの容量が大幅に節約されます。ハイブリッド・コラム圧縮では、複数のレベルの圧縮が提供されるため、更新アクティビティが最小限の表またはパーティションに最適です。

関連項目:

- 『Oracle Database VLDBおよびパーティショニング・ガイド』

- 第16章「データ・ウェアハウスのメンテナンス」

- Oracle Database管理者ガイド

- ハイブリッド・コラム圧縮の詳細は、『Oracle Database概要』を参照してください。

ビュー

ビューは、1つ以上の表または他のビューに含まれるデータが、整形された形で表されているものです。ビューにより、問合せの出力が表として処理されます。データベースの領域は必要としません。

関連項目:
Oracle Database概要

整合性制約

整合性制約は、データベースに関連したビジネス・ルールを規定し、表中の無効な情報を防止するために使用されます。データ・ウェアハウスにおける整合性制約は、OLTP環境における制約とは異なります。OLTP環境では、主として無効なデータがレコードに挿入されるのを防止しますが、データ・ウェアハウス環境では、正確さがすでに保証されているため、これは大きな問題ではありません。データ・ウェアハウス環境では、制約はクエリー・リライトにのみ使用されます。NOT NULL制約は、データ・ウェアハウスでは特に一般的です。ある特定の状況では、制約はデータベースの領域を必要とします。このような制約は、基礎となる一意索引の形式になっています。

関連項目:
第8章「整合性制約」

索引とパーティション索引

索引は、表またはクラスタに関連付けられたオプションの構造体です。データ・ウェアハウス環境では、従来のBツリー索引に加えて、ビットマップ索引がきわめて一般的です。ビットマップ索引は、セット指向の操作に最適化された索引構造です。また、スター型変換など、最適化された一部のデータ・アクセス方法にも必要になります。

索引は、パーティション化できるという点では表と同じですが、パーティション化方法は表の構造に依存しません。索引をパーティション化すると、リフレッシュ時にデータ・ウェアハウスを管理しやすくなり、問合せのパフォーマンスが改善されます。

関連項目:
第7章「索引」および第16章「データ・ウェアハウスのメンテナンス」

マテリアライズド・ビュー

マテリアライズド・ビューには、実行に時間のかかるSQL文の計算が不要になるように、事前の問合せ結果が格納されています。物理設計の観点からすると、マテリアライズド・ビューは表やパーティション表に似ており、透過的に使用されパフォーマンスが向上するという点で、索引のような動作をします。

関連項目:
第9章「基本的なマテリアライズド・ビュー」

ディメンション

ディメンションは、列または列セット間の階層関係を定義するスキーマ・オブジェクトです。階層関係とは、階層内のあるレベルのデータが次のレベルのどのデータに結びつくかという依存関係を指します。ディメンションは論理関係のコンテナであり、データベースに領域を必要としません。典型的なディメンションには、市、州(または県)、地域および国などがあります。

関連項目:
第11章「ディメンション」

4 データ・ウェアハウスにおけるハードウェアおよびI/Oの考慮事項

この章では、データ・ウェアハウス環境におけるハードウェア問題およびI/O問題について説明します。内容は次のとおりです。

- データ・ウェアハウスにおけるハードウェアおよびI/Oの考慮事項の概要

- 記憶域管理

データ・ウェアハウスにおけるハードウェアおよびI/Oの考慮事項の概要

データ・ウェアハウスの設計者および管理者は、I/Oのパフォーマンスを常に考慮する必要があります。データ・ウェアハウスにおける典型的なワークロードとして特にあげられるのは、I/Oの集中です。I/Oの集中は、大量のデータ・ロード、索引の構築、マテリアライズド・ビューの作成、大量のデータに対する問合せなどの操作で発生します。データ・ウェアハウスの基礎となるI/Oシステムは、これらの厳しい要件を満たすように設計する必要があります。

実際、貧弱なI/O構成が、データ・ウェアハウスにおけるパフォーマンス上の問題の主な原因の1つとなっています。別のシステムの管理経験があるデータベース管理者がデータ・ウェアハウスを管理する場合は、以前の環境よりもI/O構成に慎重になる必要があります。

この章では、データ・ウェアハウスのI/O構成に関する次の5つの高度なガイドラインを示します。

- 容量ではなく帯域幅に基づくI/Oの構成

- 幅広いストライプ化

- 冗長性の使用

- データベース構築前のI/Oシステムのテスト

- システム拡張のための計画

データ・ウェアハウスで使用するI/O構成は、特定の記憶域およびサーバーの性能特性によって異なります。したがってこの章の内容は、I/Oシステムの設計およびチューニングのガイドラインとしてのみ利用してください。

参照:
I/O構成およびチューニングの追加情報は、『Oracle Databaseパフォーマンス・チューニング・ガイド』を参照してください。

容量ではなく帯域幅に基づくI/Oの構成

データ・ウェアハウスの記憶域構成は、その記憶域構成が提供可能なI/O帯域幅に基づいて選択する必要があり、必ずしも記憶域全体の容量に基づく必要はありません。容量のみに基づいて記憶域を確保すると、特に合計で500GBに満たないシステムにおいて、十分なパフォーマンスが得られない場合があります。個々のディスク・ドライブの容量は、そのI/Oスループットの向上ペースを上回るペースで大きくなっています。そのため、少数のディスクで大量のデータを格納できる一方、それらのディスクでは多数の少容量ディスクと同じI/Oスループットを提供できなくなっています。

たとえば、200GBのデータ・マートがあるとします。72GBのドライブを使用すれば、たった6台のドライブで完全にミラー化されたデータ・マートを構成できます。しかし、6台のドライブでは、4CPUのサーバーに対する平均的な同時ユーザー数に対応できるだけのI/O帯域幅を提供できない可能性もあります。このように、6台のドライブで十分な記憶領域を用意できるにもかかわらず、このシステムに十分なパフォーマンスを提供するために、より多くのドライブが必要になる場合があります。

システムを構築する前にデータ・ウェアハウスで必要となるI/O帯域幅を見積っても、あまり意味がない可能性があります。しかし一般的に、ハードウェア・ベンダーのサポートの下で、使用するサーバーで利用できるI/O帯域幅を見積り、選択したI/O構成でそのサーバーの性能を十分に引き出せるかどうかを確認することは有効です。I/Oシステムのサイズ設定に関する変数は数多くありますが、基本的な経験則として、データ・ウェアハウス・システムでパフォーマンスを最適化するには、CPUごとに複数のディスク(各CPUに少なくとも2台のディスク)が必要というルールがあります。

幅広いストライプ化

データ・ウェアハウスのI/Oシステムを構成する際の原則は、複数のディスクおよびチャネルが各データベース・オブジェクトにアクセスするようにして、I/O帯域幅を最大化することです。これは、Oracle Databaseのデータファイルをストライプ化することで実現できます。ストライプ化された1つのファイルは、複数のディスクに分散して格納されます。ストライプ化は、ソフトウェア(論理ボリューム・マネージャなど)によって、または記憶域ハードウェア内部で管理できます。その目的は、各表領域を多数のディスク(理想的にはすべてのディスク)にわたってストライプ化することにより、可能なかぎり最大のI/O帯域幅であらゆるデータベース・オブジェクトにアクセスできるようにすることです。

冗長性の使用

多くの場合、データ・ウェアハウスは企業内で最大のデータベース・システムです。そのため、備えているディスクの数は最も多く、1つのディスクに障害が発生した際の影響は最も大きくなります。したがって、データ・ウェアハウスをハードウェア障害から保護するために、ディスクの冗長性を高める必要があります。ディスクのストライプ化と同様に、冗長性もソフトウェアまたはハードウェアによる様々な方法で確保できます。

重要なのは、状況に応じて冗長性とパフォーマンスのバランスを考慮する必要があるということです。たとえば、RAID 5構成の記憶域システムはRAID 0+1構成よりも低コストである可能性がありますが、パフォーマンスでは劣る場合があります。冗長性はあらゆるデータ・ウェアハウスに必要ですが、冗長性に対するアプローチは、個々のデータ・ウェアハウスのパフォーマンスおよびコストの制約によって異なります。

データベース構築前のI/Oシステムのテスト

I/Oシステムを検討およびチューニングするうえでは、データベースを作成する前の段階が最も重要です。一度データベース・ファイルを作成してしまうと、それらを再構成するのはより困難になります。ファイルの動的な再構成をサポートしている論理ボリューム・マネージャもありますが、記憶域の構成によっては、I/Oレイアウトを再構成するにはファイルを完全に再構築しなければならない場合もあります。いずれの場合も、再構成には大量のシステム・リソースを割く必要があります。

新しいシステムにデータ・ウェアハウスを構築する際は、すべてのデータベース・データファイルを作成する前にI/O帯域幅をテストして、必要なI/Oレベルを達成できるかどうかを調べる必要があります。ほとんどのオペレーティング・システムでは、サイズの大きいテスト・ファイルの読取り/書込みパフォーマンスを計測する単純なスクリプトで、これを調べることができます。

システム拡張のための計画

データ・ウェアハウスの設計者は、データ・ウェアハウスの将来的な拡張を考慮して計画を立てる必要があります。システムの拡張に対応するためのアプローチは数多くありますが、特に考慮する必要があるのは、I/O帯域幅を損うことなくI/Oシステムを拡張できるようにすることです。たとえば、20台のディスクで構成された既存のシステムに4台のディスクを追加し、4台の新しいディスクのみにわたってストライプ化された表領域を追加しても、データベースを拡張できません。より適切な解決策は、24台のディスクすべてにわたってストライプ化された表領域を新たに追加し、20台のディスクにストライプ化された既存の表領域を、24台のディスクすべてにストライプ化されるように徐々に変換していくことです。

記憶域管理

ディスクの管理に関する機能として、Oracle Managed Filesと自動ストレージ管理という2つの機能があります。これらの機能を使用しない場合、データベース管理者は、データ・ウェアハウスでは数百から数千にも及ぶデータベース・ファイルを自分で管理する必要があります。Oracle Managed Filesは、ファイルを自動的に作成および管理する機能を提供することによって、データベースの管理を簡略化します。このためデータベース管理者は、データベース・ファイルを個々に管理する必要がありません。自動ストレージ管理は、ファイルのみでなくディスクも管理する追加機能を提供します。自動ストレージ管理では、データベース管理者は少数のディスク・グループを管理します。自動ストレージ管理は、ストライプ化やディスク冗長性の提供などのタスクを行います。この中には、新しいディスクがシステムに追加された場合にデータベース・ファイルのバランスを再調整する機能も含まれます。

参照:
詳細は、『Oracle Databaseストレージ管理者ガイド』を参照してください。

5 データ・ウェアハウスにおけるパーティション化

この章では、データ・ウェアハウス環境でのパーティション化の概要について説明します。内容は次のとおりです。

- データ・ウェアハウスにおけるパーティション化の概要

データ・ウェアハウスにおけるパーティション化の概要

データ・ウェアハウスには非常に大きな表が含まれることが多く、これらの大規模な表を管理し、最適な問合せパフォーマンスを得るためのテクニックが必要とされます。これの実現に加え、データ・アクセスを向上させたり、全体的なアプリケーション・パフォーマンスを改善するために重要となるツールが、パーティション化です。

パーティション化では、非常に大きな表および索引をパーティションと呼ばれるより小さくて管理しやすい部分に分解します。そのため、特に数百万行および数GBのデータを含む表および索引にアクセスするアプリケーションに対して有効です。パーティション表およびパーティション索引を使用すると、データのサブセット上で管理操作を実行できるため、管理操作が簡単になります。たとえば、読取り専用アプリケーションへの割込みをゼロまたは最小限に抑えて、新しいパーティションの追加、既存のパーティションの編成またはパーティションの削除を行うことができます。

パーティション化は、SQL文をチューニングして、(パーティション・プルーニングの使用による)不要な索引および表のスキャンを回避する場合に効果的です。また、パーティション・ワイズ結合を使用して大量のデータ(たとえば、数百万行)を結合する場合の、大規模な結合操作のパフォーマンスを改善することもできます。さらに、データのパーティション化によって、非常に大規模なデータベースの管理が大幅に改善され、バックアップやリストアなどの管理作業に必要な時間が大幅に削減されます。

パーティションを追加または作成するとき、データが最初に挿入されるまで、セグメント作成を遅延させるオプションがあります。これは大きなフットプリントのあるアプリケーションをインストールする場合に特に有用です。

パーティション化方法における最小単位は、パーティションを分割したりマージしたりすることで簡単に変更できます。つまり、表の一部のパーティションにデータが偏っているような場合に、多くのデータが含まれているパーティションを分割してデータをより均等に分散させることが可能です。また、パーティション化すると、パーティションを表とスワップできます。大量のデータをすばやく簡単に追加、削除またはスワップできるようになるため、スワッピングを利用してロードが完了するまでロード中の大量データをアクセス不可にしたり、様々な使用フェーズ間でデータをステージングする手段としてスワッピングを使用することができます。たとえば、当日のトランザクションやオンライン・アーカイブは、その一例です。

パーティション化方法を検討するにあたっては、まずTuning Packに含まれているSQLアクセス・アドバイザのパーティション化アドバイスを使用することをお薦めします。SQLアクセス・アドバイザでは、グラフィカル・インタフェースとコマンドライン・インタフェースの両方を使用できます。

一般的にパーティション化とともに使用される補足的な方法はパラレル実行です。これは長時間問合せ、ETLおよびその他の操作を高速化します。パラレル・ステートメントの負荷が非常に高いデータ・ウェアハウスでは、パラレル・ステートメント・キューイングを使用して自動的にパラレル・ステートメントを管理できます。

参照:

- 第6章「データ・ウェアハウスにおけるパラレル実行」

- パラレル実行およびパラレル・ステートメント・キューイングに加えパーティション化の使用方法と使用タイミングの詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

- SQLアクセス・アドバイザの詳細は、『Oracle Database 2日でパフォーマンス・チューニング・ガイド』を参照してください。

6 データ・ウェアハウスにおけるパラレル実行

この章では、データ・ウェアハウスでパフォーマンスを向上させることのできるパラレル実行の概念について説明します。次の内容が含まれます。

- パラレル実行の概要

- パラレル実行を使用する理由

- 自動並列度および文のキューイング

- メモリー内パラレル実行

パラレル実行の概要

今日のデータベースには、それがウェアハウスや運用データ・ストア、またはOLTPシステムのどれであるかに限らず、大量の情報が格納されています。しかし、含まれるデータの量が膨大であるため、正しい情報をタイムリーに検索し、提示することは容易ではありません。

パラレル実行は、この課題に対処するための機能です。パラレル実行(パラレル化とも呼ばれます)では、複数のプロセスを使用して単一のタスクを完了させることで、テラバイトのデータを数時間や数日ではなく、数分で処理できます。これにより、通常意思決定支援システム(DSS)およびデータ・ウェアハウスに関連付けられているサイズの大きなデータベース上で、データ集中型の操作のレスポンス時間を大幅に削減できます。また、OLTPシステム上で、索引の作成などのバッチ処理またはスキーマ・メンテナンス操作のためにパラレル実行を実装することもできます。パラレル化の概念は、タスクへの分解であり、これにより1つのプロセスで問合せに関するすべての処理を実行するのではなく、多くのプロセスが同時に各処理を実行します。たとえば、1年の合計売上げを4つのプロセスで計算するのに、1つのプロセスですべての四半期を処理するのではなく、各プロセスが1年の四半期それぞれを処理する場合です。これを使用するとパフォーマンスの大幅な向上が見込めます。

パラレル実行では、次のプロセスのパフォーマンスを向上できます。

- サイズの大きな表のスキャン、結合またはパーティション化された索引のスキャンを要求する問合せ

- 大規模な索引の作成

- サイズの大きな表の作成(マテリアライズド・ビューを含む)

- バルク挿入、更新、マージ、削除

また、パラレル実行を使用して、Oracleデータベース内のオブジェクト型にアクセスできます。たとえば、パラレル実行により、ラージ・オブジェクト(LOB)にアクセスできます。

大規模なデータ・ウェアハウスでは、優れたパフォーマンスを得るために、常にパラレル実行を使用する必要があります。OLTPアプリケーションの特定の処理(バッチ処理など)も、パラレル実行を行うことでパフォーマンスが大幅に向上します。

パラレル実行を使用する理由

通りにある車の台数を数えるというタスクがあるとします。このタスクを実行するには2つの方法があります。1つは、自分が通りまで行き、車の台数を数えます。もう1つは、友人に協力してもらい、それぞれ通りの反対側から車の台数を数え始め、2人が落ち合ったところでそれぞれの台数を足します。

友人が車を数える速さがあなたと同じだとした場合、通りにあるすべての車を数えるタスクは、自分一人で行う場合と比べて半分の時間で終えることができます。このような場合、処理は直線的に測定できます。つまりリソースの数を2倍にすると、全体の処理時間は半分になります。

データベースの場合も、車を数える例とさほど変わりません。リソースの数を2倍にすることで、処理時間が最初の半分になれば、その処理は直線的に測定できます。直線的に測定できることが、車を数える場合でも、データベース問合せからの応答を配信する場合でも、パラレル処理の最終的な目標です。

関連項目:

- パラレル化の概念の一般的な説明は、『Oracle Database概要』を参照してください。

- パラレル実行の使用に関する詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

パラレル実行を実装する場合

パラレル実行は、次のすべての特性を持つシステム上で有効です。

- 対称型マルチプロセッサ(SMP)、クラスタ、または大規模なパラレル・システム

- 十分なI/Oバンド幅

- 稼働中でないCPUまたは断続的に使用されているCPU(CPUの使用率が通常30%未満のシステムなど)

- ソート、ハッシュおよびI/Oバッファなどの追加のメモリー集中処理をサポートする十分なメモリー

これらの特性を持たないシステムでは、パラレル実行を使用しても大幅にパフォーマンスが向上しない場合があります。実際に、大容量を使用しているシステムまたはI/Oバンド幅の少ないシステム上では、システムのパフォーマンスが低下する可能性があります。

パラレル実行のメリットは、DSSおよびデータ・ウェアハウス環境でわかります。OLTPシステムでも、索引の作成などのスキーマ・メンテナンス操作中やバッチ処理中に、パラレル実行によるメリットを得ることができます。OLTPアプリケーションを特徴づける通常の単純なDMLまたはSELECT文では、パラレルで実行することによるメリットはありません。

パラレル実行を実装しない場合

パラレル実行は、次の場合では通常有効ではありません。

- 標準的な問合せまたはトランザクションが非常に短い(数秒またはそれ以下)環境。これには、ほとんどのオンライン・トランザクション・システムが含まれます。パラレル実行サーバーの調整にはコストがかかり、短いトランザクションでは、このような調整のコストがパラレル化のメリットよりも上回る可能性があるため、パラレル実行はこのような環境では有効ではありません。

- CPU、メモリーまたはI/Oリソースが大量に使用されている環境。パラレル実行は追加の使用可能なハードウェア・リソースを利用するように設計されています。そのようなリソースが使用できない場合、パラレル実行はなんのメリットももたらさず、パフォーマンスに悪影響をおよぼす可能性があります。

自動並列度および文のキューイング

名前が示すとおり、自動並列度では、オプティマイザにより決定された最速と考えられるプランに基づき、Oracle Databaseが文(DML、DDLおよび問合せ)を実行する並列度(DOP)を決定します。つまり、データベースが問合せを解析し、コストを計算してから、実行に使用するDOPを計算します。コストの最も低い計画がシリアルに実行される可能性もオプションの1つとしてあります。図6-1「オプティマイザの計算: シリアルかパラレルか」に、この決定プロセスを示します。

図6-1 オプティマイザの計算: シリアルかパラレルか

自動DOPの使用を選択する場合、特にしきい値が比較的低い場合は、多くの文がパラレルに実行している可能性があります。この場合の低いというのはシステムに対する相対的なものであり、絶対的な数量ではありません。

自動DOPを使用して多くの文がパラレルに実行しているという動作が予想されるため、使用可能なパラレル・プロセスの使用量を管理することはより重要になります。つまり、システムは、文をいつ実行するかについてインテリジェントであり、要求されたパラレル・プロセスの数が使用可能かどうかを検証する必要があります。この要求されたプロセスの数というのが、その文のDOPです。

この管理上の課題への答は、データベース・リソース・マネージャを使用した文のパラレル・キューイングです。文のパラレル・キューイングでは、文が要求したDOPが使用可能な場合に文が実行されます。たとえば、文が64のDOPを要求したときに、この顧客への支援に現在使用できるのが32プロセスのみである場合は、この文はキューに入れられます。

データベース・リソース・マネージャを使用すると、文を消費者グループを介してワークロードに分類できます。各消費者グループには適切な優先度およびパラレル・プロセスの適切なレベルが与えられます。また、各消費者グループはシステム・ロードに基づきパラレル文を格納する固有のキューを持ちます。

関連項目:

- パラレル実行での自動DOPの使用の詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

- データベース・リソース・マネージャの使用の詳細は、『Oracle Database管理者ガイド』を参照してください。

メモリー内パラレル実行

従来、ほとんどの操作に対するパラレル処理では、データベース・バッファ・キャッシュがバイパスされ、ディスクから(ダイレクト・パスI/O経由で)パラレル実行サーバーのプライベート作業領域に直接読み込まれていました。DB_CACHE_SIZEの約2%よりも小さいオブジェクトのみがインスタンスのデータベース・バッファ・キャッシュに入れられますが、パラレルにアクセスされるオブジェクトはこの制限よりも大きいものがほとんどです。つまりこの動作は、そのプライベート処理以外では、使用可能なメモリが利用されることがほとんどないことを示します。しかし、この10年で、ハードウェア・システムはきわめて劇的に進化し、一般的なデータベース・サーバーのメモリ容量は2桁または3桁のギガバイトの領域に達しています。このことが、Oracleの圧縮技術と、Oracle Real Application Clustersの集計されたデータベース・バッファ・キャッシュを利用するOracle Database 11gリリース2の機能とともに、テラバイトの領域でのオブジェクトのキャッシュを可能にしました。

メモリ内パラレル実行は、この大きな集計されたデータベース・バッファ・キャッシュを利用します。パラレル実行サーバーはデータベース・バッファ・キャッシュを使用してオブジェクトにアクセスすることにより、ディスク上よりも少なくとも10倍は高速でスキャンできます。

メモリ内パラレル実行を使用して、SQL文がパラレルに発行されるとき、チェックが実行されて、文がアクセスするオブジェクトをシステムの集計されたバッファ・キャッシュに入れる必要があるかどうかが決定されます。この状況で、オブジェクトは表、索引またはパーティション・オブジェクトの場合には1つまたは複数のパーティションのいずれかです。

関連項目:

- メモリー内パラレル実行の使用の詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

7 索引

この章の内容は次のとおりです。

- データ・ウェアハウスでのビットマップ索引の使用

- データ・ウェアハウスでのBツリー索引の使用

- 索引の圧縮の使用

- ローカル索引とグローバル索引の選択基準

参照:
索引付けの概要は、『Oracle Database概要』を参照してください。

データ・ウェアハウスでのビットマップ索引の使用

ビットマップ索引は、データ・ウェアハウス環境で広く使用されています。通常、データ・ウェアハウス環境では、大量のデータおよび非定型の問合せがありますが、DMLトランザクションが同時に発生することは稀です。このようなアプリケーションでは、ビットマップ索引による次のメリットがあります。

- 大規模な非定型問合せに対する応答時間が削減されます。

- 他の索引付けテクニックと比較すると、領域の使用量が少なくて済みます。

- 比較的CPUの数が少ないハードウェアまたはメモリー量が少ないハードウェアでも、大幅にパフォーマンスが向上します。

- パラレルDMLおよびロード中に、効率的にメンテナンスができます。

大規模な表を従来のBツリー索引で完全に索引付けすると、索引が、表にあるデータの数倍の大きさになる場合があるため、ディスク領域の点で非常にコストが高くなります。通常、ビットマップ索引のサイズは、表内の索引付けされたデータの何分の1かの大きさで済みます。

索引は、指定したキー値を含む表の行へのポインタを保有します。通常の索引には、そのキー値がある行に対応する各キーのROWIDのリストが格納されます。ビットマップ索引では、各キー値のビットマップが、ROWIDのリストのかわりに使用されます。

ビットマップの各ビットは、ROWIDに対応します。ビットが設定されると、対応するROWIDを持つ行に、キー値が含まれることを意味します。マッピング機能によってビットの位置が実際のROWIDに変換されるため、ビットマップ索引は、通常の索引と同じ機能を提供します。ビットマップ索引は、ビットマップを圧縮して格納します。個別キー値の数が少ない場合は、ビットマップ索引の圧縮率が高くなり、Bツリー索引に対する領域節約の面での優位性がさらに高くなります。

ビットマップ索引は、WHERE句に複数の条件が含まれる問合せに対して最も効率的です。すべての条件ではなく一部の条件のみを満たす行は、表自体がアクセスされる前に除外されます。これによって、応答時間が大幅に削減されます。どの索引を作成するべきか判断できない場合は、SQLアクセス・アドバイザを使用すると、作成するべき索引についてのリコメンデーションが生成されます。ビットマップ索引のビットマップは瞬時に結合されるので、通常は単一列のビットマップ索引を使用するのが最適です。

ビットマップ索引を作成するときは、NOLOGGINGおよびCOMPUTE STATISTICSを使用する必要があります。また、ビットマップ索引は、通常、メンテナンスを行うよりも削除して再作成するほうが簡単であることに注意してください。

データ・ウェアハウス・アプリケーションに対するメリット

ビットマップ索引は、ユーザーがデータの更新ではなく、データの問合せを行うデータ・ウェアハウス・アプリケーションに使用します。この種の索引は、データを変更する同時トランザクションの数が多いOLTPアプリケーションには適していません。

パラレル問合せおよびパラレルDMLは、ビットマップ索引でも動作します。ビットマップ索引では、索引のパラレル作成、連結索引もサポートされます。

関連項目:
データ・ウェアハウス環境におけるビットマップ索引の使用方法の詳細は、第20章「スキーマのモデリング化技法」を参照してください。

カーディナリティ

ビットマップ索引は、表の行数に対する個別値の数の比率が小さい列に対して最も効果的です。この比率をカーディナリティ度と呼びます。性別の列は個別値が2つ(男性と女性)しかないため、ビットマップ索引に最適です。ただし、データ・ウェアハウス管理者は、カーディナリティが高い列にビットマップ索引を作成する場合もあります。

たとえば、行が100万ある表では、10,000の個別値を持つ列がビットマップ索引の候補になります。この列のビットマップ索引は、特に、この列が他の索引付けされた列と連結して頻繁に問い合せられる場合に、Bツリー索引よりパフォーマンスが高くなります。実際、典型的なデータ・ウェアハウス環境では、すべての非一意列がビットマップ索引の候補です。

Bツリー索引は、カーディナリティが高いデータ(customer_nameやphone_numberなど、固有な値を多く持つデータ)に対して最も効果的です。データ・ウェアハウスでは、Bツリー索引は、一意の列またはカーディナリティが非常に高い列(ほとんど一意である列)にのみ使用してください。データ・ウェアハウスの索引は、ほとんどがビットマップ索引であっても問題ありません。

非定型問合せなどを行う場合、ビットマップ索引によって問合せのパフォーマンスが大幅に向上します。結果のビットマップをROWIDに変換する前に、対応するブール操作をビットマップに対して直接実行することにより、問合せのWHERE句で指定したANDおよびOR条件は、すぐに解決されます。結果の行数が少ない場合は、全表スキャンを行うことなく、すぐに問合せの結果が戻されます。

例7-1 ビットマップ索引

次の例は、ある会社のcustomers表の一部を示しています。

SELECT cust_id, cust_gender, cust_marital_status, cust_income_level
FROM customers;

CUST_ID    C CUST_MARITAL_STATUS  CUST_INCOME_LEVEL
---------- - -------------------- ---------------------
...
70 F                      D: 70,000 - 89,999
80 F married              H: 150,000 - 169,999
90 M single               H: 150,000 - 169,999
100 F                      I: 170,000 - 189,999
110 F married              C: 50,000 - 69,999
120 M single               F: 110,000 - 129,999
130 M                      J: 190,000 - 249,999
140 M married              G: 130,000 - 149,999
...

cust_gender、cust_marital_statusおよびcust_income_levelは、すべてカーディナリティが低い列(MARITAL_STATUSは3つの値のみ、GENDERは2つの値のみ、INCOME_LEVELは12の値のみが存在する列)であるため、これらの列にはビットマップ索引が理想的です。cust_idは一意の列であるため、この列にはビットマップ索引を作成しないでください。かわりに、この列に一意のBツリー索引を作成すると、最も効率的に表示および検索できます。

表7-1に、この例のcust_gender列に対するビットマップ索引を示します。この索引は、2つの別々のビットマップで構成されており、それぞれが性別に対応しています。

表7-1 ビットマップ索引の例

gender='M'

gender='F'

cust_id 70

0

1

cust_id 80

0

1

cust_id 90

1

0

cust_id 100

0

1

cust_id 110

0

1

cust_id 120

1

0

cust_id 130

1

0

cust_id 140

1

0

ビットマップの各エントリ(ビット)は、customers表の1つの行に対応します。各ビットの値は、表にある対応する行の値に依存します。たとえば、ビットマップcust_gender='F'には、最初のビットとして1が含まれています。これは、customers表の最初の行にある性別がFであるためです。ビットマップcust_gender='F'の第3ビットは0です。これは、3行目の性別がFでないためです。

会社の顧客の人口統計情報傾向を調査するアナリストが、「既婚者で、所得レベルがGまたはHの顧客がどれだけいるか」と質問するとします。この質問は、次の問合せで表すことができます。

SELECT COUNT(*) FROM customers
WHERE cust_marital_status = 'married'
AND cust_income_level IN ('H: 150,000 - 169,999', 'G: 130,000 - 149,999');

図7-1に示すように、ビットマップ索引は、単にビットマップにある1の数をカウントすることで、この問合せを効率的に処理できます。結果セットはビットマップのORマージ操作を使用して検索され、ROWIDへの変換は不要です。さらに条件を満たす特定の顧客属性も確認するには、ビットマップからROWIDへの変換後に、結果ビットマップを使用して表にアクセスします。

図7-1 ビットマップ索引を使用した問合せの実行

ビットマップ索引の使用対象の判定方法

ファクト表のみが問い合され、索引付けされた列に対する条件がある場合、またはファクト表が2つ以上のディメンション表と結合され、そのファクト表の外部キー列に対する索引があり、ディメンション表の列に対する条件がある場合に、ビットマップ索引は有効です。

次の条件が当てはまる場合、ファクト表の列はビットマップ索引の候補となります。

- 索引付けされた列の各個別値に対し行が100以上ある。この制限が当てはまる場合、ビットマップ索引は通常の索引よりも大幅に小さくなり、通常の索引よりも早く作成できます。たとえば、数十億の行がある表に、100万の個別値が含まれる場合などです。

また、次のいずれかの場合も該当します。

- 索引付けされた列が、問合せで制限される(WHERE句で参照される)。

または

- 索引付けされた列が、ディメンション表の外部キーである。この場合、この索引によってスター型変換が行われる可能性が高くなります。

ビットマップ索引およびNULL

ビットマップ索引は、他のほとんどのタイプの索引とは異なり、NULL値を持つ行を含みます。NULLの索引付けは、集計関数COUNTが指定されている問合せなどの、いくつかのタイプのSQL文に有効です。

例7-2 ビットマップ索引

SELECT COUNT(*) FROM customers WHERE cust_marital_status IS NULL;

この問合せでは、cust_marital_statusのビットマップ索引が使用されます。この問合せでは、Bツリー索引を使用できないことに注意してください。Bツリー索引にはNULL値は格納されないためです。

SELECT COUNT(*) FROM customers;

NULLデータを持つ行を含めて、表のすべての行に索引が付けられるため、すべてのビットマップ索引をこの問合せに使用できます。NULLに索引が付けられていない場合、オプティマイザは、NOT NULL制約が指定されている列にある索引のみを使用できます。

パーティション表のビットマップ索引

ビットマップ索引をパーティション表に作成できますが、パーティション表に対してローカルである必要があり、グローバル索引にできません。パーティション表に作成できるグローバル索引は、パーティション化または非パーティション化のBツリー索引のみです。

関連項目:

- 詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

データ・ウェアハウスでのビットマップ結合索引の使用

単一表のビットマップ索引の他に、ビットマップ結合索引を作成できます。これは、複数の表の結合に対するビットマップ索引です。ビットマップ結合索引では、索引付けされる表のビットマップは、結合先の表の値のために作成されます。データ・ウェアハウス環境では、結合条件はディメンション表の主キー列とファクト表の外部キー列の間の内部等価結合です。

ビットマップ結合索引を使用すると、パフォーマンスを大幅に向上させることができます。結合結果を格納することで、ビットマップ結合索引を使用するSQL文では、結合をまったく行わずに済みます。また、ビットマップ結合索引では、結合列に対する通常のビットマップ索引と比較して、個別値の数がさらに少なくなることがほとんどなので、ビットマップの圧縮率が高くなります。その結果、結合列に対する通常のビットマップ索引よりも、使用する領域が小さくなります。

ビットマップ結合索引では、事前に結合を行うもう1つの方法であるマテリアライズド結合ビューよりも、格納効率がはるかに高くなります。これは、マテリアライズド結合ビューでは、ファクト表のROWIDが圧縮されないためです。

Bツリー索引とビットマップ索引では、最大列数の制限が異なります。

関連項目:

- これらの制限の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

ビットマップ結合索引の4つの結合モデル

ビットマップ結合索引は、スター・モデル環境で使用するのが最も一般的です。スター・モデル環境とは、大規模な表の索引列に、1つ以上の小規模な表が結合されている環境のことです。大規模な表のことをファクト表、小規模な表のことをディメンション表と呼びます。次の項では、ビットマップ結合索引でサポートされている4つの結合モデルについて説明します。スキーマのモデリング化技法については、第20章「スキーマのモデリング化技法」を参照してください。

例7-3 ビットマップ結合索引: 1つのディメンション表の列と1つのファクト表の結合

ビットマップ索引をcustomers表のcust_gender列に作成した「ビットマップ索引」の例とは異なり、ここでは、結合列customers(cust_gender)のビットマップ結合索引をファクト表salesに作成します。表salesは、次のようにcust_id値のみを格納します。

SELECT time_id, cust_id, amount_sold FROM sales;

TIME_ID   CUST_ID    AMOUNT_SOLD
--------- ---------- -----------
01-JAN-98      29700        2291
01-JAN-98       3380         114
01-JAN-98      67830         553
01-JAN-98     179330           0
01-JAN-98     127520         195
01-JAN-98      33030         280
...

このようなビットマップ結合索引を作成するには、列customers(cust_gender)を表salesに結合する必要があります。結合条件は、ビットマップ結合索引のCREATE文の一部として指定します。

CREATE BITMAP INDEX sales_cust_gender_bjix
ON sales(customers.cust_gender)
FROM sales, customers
WHERE sales.cust_id = customers.cust_id
LOCAL NOLOGGING COMPUTE STATISTICS;

次の問合せにより、ビットマップ結合索引に格納されるビットマップの作成に使用する結合結果が示されます。

SELECT sales.time_id, customers.cust_gender, sales.amount_sold
FROM sales, customers
WHERE sales.cust_id = customers.cust_id;

TIME_ID   C AMOUNT_SOLD
--------- - -----------
01-JAN-98 M        2291
01-JAN-98 F         114
01-JAN-98 M         553
01-JAN-98 M           0
01-JAN-98 M         195
01-JAN-98 M         280
01-JAN-98 M          32
...

表7-2に、この例におけるビットマップ結合索引のビットマップ表現を示します。

表7-2 ビットマップ結合索引の例

cust_gender='M'

cust_gender='F'

sales record 1

1

0

sales record 2

0

1

sales record 3

1

0

sales record 4

1

0

sales record 5

1

0

sales record 6

1

0

sales record 7

1

0

次の例のように、複数の列または複数の表を使用すると、他のビットマップ結合索引を作成できます。

例7-4 ビットマップ結合索引: 複数のディメンション列と1つのファクト表の結合

ビットマップ結合索引は、次の例のように、1つのディメンション表の複数の列に作成できます。次の例では、shスキーマのcustomers(cust_gender, cust_marital_status)を使用しています。

CREATE BITMAP INDEX sales_cust_gender_ms_bjix
ON sales(customers.cust_gender, customers.cust_marital_status)
FROM sales, customers
WHERE sales.cust_id = customers.cust_id
LOCAL NOLOGGING COMPUTE STATISTICS;

例7-5 ビットマップ結合索引: 複数のディメンション表と1つのファクト表の結合

ビットマップ結合索引は、次の例のように、複数のディメンション表に作成できます。次の例では、customers(gender)とproducts(category)を使用しています。

CREATE BITMAP INDEX sales_c_gender_p_cat_bjix
ON sales(customers.cust_gender, products.prod_category)
FROM sales, customers, products
WHERE sales.cust_id = customers.cust_id
AND sales.prod_id = products.prod_id
LOCAL NOLOGGING COMPUTE STATISTICS;

例7-6 ビットマップ結合索引: スノーフレーク・スキーマ

複数の表のビットマップ結合索引を作成できます。この場合、索引付けされた列は、別の表を使用して索引付けされた表に結合されます。たとえば、countries表がsales表と直接結合されていなくても、countries.country_nameの索引を作成できます。かわりに、countries表がcustomers表に結合され、customers表はsales表に結合されます。このタイプのスキーマは、一般にスノーフレーク・スキーマと呼ばれます。

CREATE BITMAP INDEX sales_co_country_name_bjix
ON sales(countries.country_name)
FROM sales, customers, countries
WHERE sales.cust_id = customers.cust_id
AND customers.country_id = countries.country_id
LOCAL NOLOGGING COMPUTE STATISTICS;

ビットマップ結合索引の制限事項と要件

結合結果を格納する必要があるため、ビットマップ結合索引には次の制限事項があります。

- パラレルDMLはファクト表でのみサポートされます。ディメンション表に対してパラレルDMLを行うと、索引はUNUSABLEのマークが付けられます。

- ビットマップ結合索引を使用する場合、異なるトランザクションで同時に更新できる表は1つのみです。

- 結合に2回同じ表は使用できません。

- 一時表には、ビットマップ結合索引を作成できません。

- 索引の列は、すべてディメンション表の列である必要があります。

- ディメンション表の結合列は、主キー列であるか、一意制約を持つ必要があります。

- ファクト表との結合に関与しているディメンション表の1つ以上の列は、主キー列であるか、一意制約を持つ必要があります。

- ディメンション表に複合主キーがある場合、主キーの各列が結合の一部である必要があります。

- 通常のビットマップ索引の作成に関する制限事項も、ビットマップ結合索引に適用されます。たとえば、UNIQUE属性を持つビットマップ索引は作成できません。その他の制限事項については、『Oracle Database SQL言語リファレンス』を参照してください。

データ・ウェアハウスでのBツリー索引の使用

Bツリー索引は、木をさかさまにしたような構造になっています。索引の最下位層レベルには、実際のデータ値および対応する行へのポインタがあります。これは、本の索引に、各索引エントリに対応するページ数があることとよく似ています。

一般に、典型的な問合せが索引付けされた列を参照して少数の行を取り出すことがわかっている場合に、Bツリー索引を使用します。これらの問合せでは、索引を参照する方が、行を迅速に検索できます。しかし、本の索引にたとえると、本のトピックを1つずつ参照する場合、そのトピックを索引で調べてから該当ページを検索することはありません。本のすべての章を読む方が速いことになります。これと同様に、表内のほとんどの行を取り出す場合、索引を参照して表の行を検索するのでは意味がありません。かわりに、表を読み込むかまたはスキャンします。

データ・ウェアハウスでは、一意キーの索引付けに最もよく使用されるのはBツリー索引です。多くの場合、データ・ウェアハウスにあるこれらの列に索引付けを行う必要はありません。これは、事前に実行されるETL処理の中で一意性はすでに確保されており、通常のデータ・ウェアハウスの問合せは、こうした索引を使用してもパフォーマンスが向上しないためです。Bツリー索引は、第3正規形のスキーマを使用する環境で使用するのが一般的です。ほとんどのデータ・ウェアハウス環境では、ビットマップ索引が、Bツリー索引よりもよく使用されます。

Bツリー索引とビットマップ索引では、最大列数の制限が異なります。これらの制限の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

索引の圧縮の使用

ビットマップ索引は常に、ユーザーの操作を必要とすることなく、独自の方法で圧縮されて格納されます。Bツリー索引は、明示的に圧縮して格納することで、領域を大幅に節約することが可能です。この際、各索引ブロックにはより多くのキーが格納され、I/Oの削減およびパフォーマンスの向上にもつながります。

キーの圧縮によって、Bツリー索引を圧縮できます。この結果、重複値による領域のオーバーヘッドが低減されます。非一意索引では、すべての索引列を圧縮形式で格納できますが、一意索引では、少なくとも1つの索引列は圧縮せずに格納する必要があります。

通常、索引のキーは、グループ化要素と一意要素という2つの要素を持っています。一意要素を持つようにキーが定義されていない場合は、ROWIDをグループ化要素に追加する形で一意要素が提供されます。キーの圧縮では、グループ化要素が分離され、複数の一意要素が共有できるような形で格納されます。圧縮対象として選択された列のカーディナリティによって、実現可能な圧縮率が決まります。したがって、たとえば5つの列からなる一意索引の一意性のほとんどが、後ろの2列によって実現されている場合は、先頭の3列を圧縮して格納するのが最も効率的ということになります。4つの列を圧縮する場合は、重複性はほとんど失われ、圧縮率が低下します。

キーの圧縮によって、索引の領域要件は低くなりますが、索引スキャン時のキー列値の再構築に必要なCPUタイムが長くなる場合があります。また、接頭辞エントリごとに4バイトのオーバーヘッドが生じるので、追加の領域オーバーヘッドも発生します。

ローカル索引とグローバル索引の選択基準

パーティション表のBツリー索引は、ローカルまたはグローバルにできます。Oracle8i以前のリリースでは、データ・ウェアハウス環境ではグローバル索引を使用しないようにお薦めしていました。これは、パーティションのDDL文(ALTER TABLE ... DROP PARTITIONなど)により索引全体が無効になり、索引の再構築に手間がかかったためです。Oracle Database 10g以降では、グローバル索引をメンテナンスしても、DDL後にUNUSABLEとマークされることはありません。この拡張により、データ・ウェアハウス環境におけるグローバル索引の効率が向上しています。

ただし、ローカル索引の方がグローバル索引より一般的です。グローバル索引を使用する必要があるのは、ローカル索引では満たせない特定の要件(非パーティション化キーの一意索引や、パフォーマンス要件など)がある場合です。

パーティション表のビットマップ索引は、常にローカルです。

8 整合性制約

この章では、整合性制約について説明します。内容は次のとおりです。

- データ・ウェアハウスで整合性制約が効果的な理由

- 制約の状態の概要

- 一般的なデータ・ウェアハウスの整合性制約

データ・ウェアハウスで整合性制約が効果的な理由

整合性制約は、データが、データベース管理者によって指定されたガイドラインに従うことを保証するためのメカニズムです。最も一般的なタイプの制約は、次のとおりです。

- 一意制約
特定の列が一意であることを保証します。

- NOT NULL制約
NULL値が許されないことを保証します。

- 外部キー制約
2つのキーが主キーと外部キーの関係を共有することを保証します。

制約は、データ・ウェアハウスにおいて、次の目的に使用できます。

- データの正当性
制約により、不適切なデータの挿入を防止するために、データ・ウェアハウスのデータがデータ整合性および正確さのガイドラインに従っているかどうかが検証されます。

- 問合せの最適化
Oracle Databaseでは、SQL問合せを最適化するときに、制約が使用されます。制約は、問合せの最適化に多くの点で効果的ですが、マテリアライズド・ビューのクエリー・リライトに特に重要です。

多くのリレーショナル・データベース環境とは異なり、データ・ウェアハウスのデータは通常、抽出、変換、ロード(ETL)プロセス中の、制御された状況下で追加または変更されます。通常、OLTPシステムとは異なり、複数のユーザーがデータ・ウェアハウスを直接更新することはありません。

参照:
第12章「抽出、変換、ロードの概要」

制約の状態の概要

データ・ウェアハウスにおいて最適な制約の使用方法を理解するには、まず、制約の基本的な目的を理解する必要があります。このような目的をいくつか次に示します。

- 施行
制約を施行するには、制約がENABLE状態である必要があります。ENABLE状態の制約により、任意の1つ(または複数)の表におけるすべてのデータの変更が、制約の条件を満たすことが保証されます。データ変更操作によってデータが制約に違反する場合、その操作は制約違反エラーとなり正常に実行されません。

- 妥当性チェック
妥当性チェックを行うために制約を使用するには、制約がVALIDATE状態である必要があります。制約がVALIDATED状態の場合、表に現在存在しているすべてのデータが制約を満たします。

妥当性チェックは、施行とは関係ありません。業務系システムでの一般的な制約はENABLEDおよびVALIDATED状態ですが、VALIDATED状態であってもENABLED状態でないか、またはその逆(ENABLED状態であってもVALIDATED状態でない)の場合もあります。後者の2つの状態は、データ・ウェアハウスで効果的です。

- 信頼
特定の制約の条件がTRUEであることがわかっているため、妥当性チェックや制約の施行を必要としない場合があります。しかしそのような場合でも、問合せの最適化やパフォーマンスの改善のために、制約を存在させることができます。この目的で使用する制約は、信頼またはRELY制約と呼ばれ、RELY状態である必要があります。RELY状態は、指定された制約がTRUEであることを信頼してよいことをOracleに通知するメカニズムを提供します。

RELY状態の影響を受けるのは、VALIDATED状態でない制約のみであることに注意してください。

一般的なデータ・ウェアハウスの整合性制約

この項では、読者が制約の一般的な使用方法を理解していることを前提としています。つまり、ENABLEかつVALIDATEDな状態の制約です。データ・ウェアハウスでは、このような制約の作成およびメンテナンスに非常にコストがかかるため、多くのユーザーにとって、このような制約が効率的でないことは明らかです。この項の内容は次のとおりです。

- データ・ウェアハウスでの一意制約

- データ・ウェアハウスでの外部キー制約

- RELY制約

- NOT NULL制約

- 整合性制約およびパラレル化

- 整合性制約およびパーティション化

- ビューの制約

データ・ウェアハウスでの一意制約

一意制約は、通常、一意索引を使用して規定されます。ただし、表が非常に大規模になる可能性があるデータ・ウェアハウスでは、一意索引を作成すると、処理時間およびディスク領域の点で非常にコストがかかる場合があります。

データ・ウェアハウスにsales表があり、その表にsales_id列が含まれているとします。sales_idは、単一の売上トランザクションを一意に識別し、データ・ウェアハウス管理者は、この列がデータ・ウェアハウス内で一意であることを保証する必要があります。

制約を作成する方法の1つを、次に示します。

ALTER TABLE sales ADD CONSTRAINT sales_uk
UNIQUE (prod_id, cust_id, promo_id, channel_id, time_id);

デフォルトでは、この制約はENABLEかつVALIDATEDな状態です。Oracleは、この制約をサポートするために、sales_idに一意索引を暗黙的に作成します。ただし、次の3つの理由から、この索引がデータ・ウェアハウスでは不適切な場合があります。

- sales表には数百万または数十億もの行が含まれることが多いため、一意索引は非常に大きくなる可能性があります。

- 一意索引は、問合せの実行にはあまり使用されません。ほとんどのデータ・ウェアハウスの問合せは一意キーについての条件検索を行わないため、この索引を作成してもパフォーマンスが改善される可能性はあまりありません。

- salesがsales_id以外の列でパーティション化されている場合は、一意索引はグローバル索引である必要があります。これによって、sales表でのすべてのメンテナンス操作が悪影響を受ける場合があります。

一意索引は、sales表で変更された個々の行が一意制約を満たすことを保証するために必要です。

データ・ウェアハウス表の場合に使用できる、一意制約にかわる方法を次の文に示します。

ALTER TABLE sales ADD CONSTRAINT sales_uk
UNIQUE (prod_id, cust_id, promo_id, channel_id, time_id) DISABLE VALIDATE;

この文によって一意キー制約が作成されますが、制約がDISABLED状態であるため、一意索引は必要ありません。この方法によって、制約が一意索引のデメリットの影響を受けずに一意性を保証できるため、多くのデータ・ウェアハウス環境でメリットがあります。

ただし、データ・ウェアハウス管理者が、DISABLE VALIDATE制約を考慮する場合にトレードオフがあります。この制約はDISABLED状態であるため、一意の列を変更するDML文はsales表に対して実行できません。制約が存在する状態でこの表を変更するには、次の2つの方法があります。

- DDLを使用して、この表にデータを追加します(パーティションの交換など)。第16章「データ・ウェアハウスのメンテナンス」の例を参照してください。

- この表を変更する前に、制約を削除します。その後、すべての必要なデータ修正を行います。最後に、DISABLED状態の制約を再作成します。DISABLED状態の制約を再作成する方が、ENABLED状態の制約を再作成するより効率的です。ただし、この方法では、制約の削除中にsales表に追加されたデータが一意であることは保証されません。

データ・ウェアハウスでの外部キー制約

スター・スキーマ・データ・ウェアハウスでは、外部キー制約により、ファクト表とディメンション表のリレーションシップの妥当性がチェックされます。制約の例を次に示します。

ALTER TABLE sales ADD CONSTRAINT sales_time_fk
FOREIGN KEY (time_id) REFERENCES times (time_id)
ENABLE VALIDATE;

ただし、場合によっては、外部キー制約に異なる状態(特に、ENABLE NOVALIDATE状態)を使用するように選択することがあります。データ・ウェアハウス管理者は、次のいずれかの場合に、ENABLE NOVALIDATE制約を使用することがあります。

- 制約を満たさないデータが表にあるが、データ・ウェアハウス管理者が規定する制約を作成する場合

- 施行済の制約がすぐに必要な場合

データ・ウェアハウスで、新しいデータはファクト表に毎日ロードされ、ディメンション表は週末にのみリフレッシュされるとします。その週の間は、ディメンション表とファクト表が実際には外部キー制約を満たさない可能性があります。それでも、データ・ウェアハウス管理者は、ETLプロセス外で外部キー制約に影響する可能性がある変更が行われないようにするため、この制約を施行する場合があります。つまり、次のように、ETLプロセスの実行後に外部キー制約を毎晩作成できます。

ALTER TABLE sales ADD CONSTRAINT sales_time_fk
FOREIGN KEY (time_id) REFERENCES times (time_id)
ENABLE NOVALIDATE;

ENABLE NOVALIDATEを使用すると、制約がTRUEであると考えられる場合にも、施行される制約をすばやく作成できます。ETLプロセスによって、外部キー制約がTRUEであるかどうかが検証されるとします。データベースにこの外部キー制約を再検証させるには時間およびデータベース・リソースが必要となるため、かわりに、データ・ウェアハウス管理者は、ENABLE NOVALIDATEを使用して外部キー制約を作成できます。

RELY制約

ETLプロセスでは、通常、ある制約がTRUEかどうかが検証されます。たとえば、ETLプロセスはファクト表の受信データにあるすべての外部キーの妥当性チェックを実行します。これは、データ・ウェアハウスに制約を実装するかわりに、制約に従ったデータが提供されることが信頼できることを意味します。RELY制約を次のように作成します。

ALTER TABLE sales ADD CONSTRAINT sales_time_fk
FOREIGN KEY (time_id) REFERENCES times (time_id)
RELY DISABLE NOVALIDATE;

この文は、主キーがRELY状態であることを前提としています。RELY制約は、データの妥当性チェックに使用されない場合にも、次のことができます。

- マテリアライズド・ビューに対して、より高度なクエリー・リライトを使用可能にします。詳細は、第18章「基本的なクエリー・リライト」を参照してください。

- その他のデータ・ウェアハウス・ツールによって、制約に関する情報をOracleデータ・ディクショナリから直接取り出すことができます。

RELY制約の作成には、コストがほとんどかかりません。また、DML中やロード中にもオーバーヘッドは発生しません。制約がVALIDATED状態ではないため、その作成に必要なデータ処理はありません。

NOT NULL制約

クエリー・リライトを使用する際は、NOT NULL制約が必要かどうかを考慮する必要があります。NOT NULL制約が必要となる典型例は、後戻り結合クエリー・リライトを使用する場合です。クエリー・リライト使用時のNOT NULL制約の詳細は、第19章「高度なクエリー・リライト」を参照してください。

整合性制約およびパラレル化

すべての制約は、パラレルで妥当性チェックできます。非常に大規模な表で制約の妥当性チェックを行う場合、パフォーマンスの目標を達成するために、パラレル化が必要になります。ある任意の制約操作の並列度は、基礎となる表のデフォルトの並列度によって決定されます。

整合性制約およびパーティション化

データをパーティション化する前に、制約を作成してメンテナンスできます。データ・ウェアハウスにおけるパーティション化の重要性については、以降の章を参照してください。パーティション化により、他の多くの操作の管理と同様に、制約管理も改善できます。たとえば、第16章「データ・ウェアハウスのメンテナンス」では、別々のステージング表に対して一意制約および外部キー制約を作成する例を示しています。これらの制約は、EXCHANGE PARTITION文の実行中にメンテナンスされます。

ビューの制約

ビューの制約を作成できます。ビューでサポートされるタイプの制約は、RELY制約のみです。

このタイプの制約は、問合せが実表ではなくビューにアクセスする際に、データベース管理者が、ビューとの間のリレーションシップを定義する必要がある場合に有効です。

関連項目:
第9章「基本的なマテリアライズド・ビュー」および第18章「基本的なクエリー・リライト」

9 基本的なマテリアライズド・ビュー

この章では、マテリアライズド・ビューの使用方法について説明します。内容は次のとおりです。

- マテリアライズド・ビューを使用したデータ・ウェアハウスの概要

- マテリアライズド・ビューのタイプ

- マテリアライズド・ビューの作成

- 既存のマテリアライズド・ビューの登録

- マテリアライズド・ビューに対する索引付けの選択

- マテリアライズド・ビューの削除

- マテリアライズド・ビュー機能の分析

マテリアライズド・ビューを使用したデータ・ウェアハウスの概要

一般的に、データは月、週または日単位で、1つ以上のオンライン・トランザクション処理(OLTP)データベースからデータ・ウェアハウスに送られます。データは通常、データ・ウェアハウスに追加される前に ステージング・ファイルで処理されます。データ・ウェアハウスのサイズは、一般的に、数十GBから数TBの範囲にわたります。通常、データの大半はいくつかの非常に大規模なファクト表に格納されます。

パフォーマンス向上のためにデータ・ウェアハウスで使用されている1つのテクニックに、サマリーの作成があります。これは、特殊なタイプの集計ビューであり、問合せを実行する前に、効率が悪い結合および集計操作を事前に計算し、その結果をデータベース内の表に格納することで、問合せ実行時間を短縮します。たとえば、サマリー表が、地域別および製品別の売上合計を含むように作成できます。

このマニュアルおよびデータ・ウェアハウスに関するマニュアルで言及されているサマリーまたは集計は、マテリアライズド・ビューと呼ばれるスキーマ・オブジェクトを使用して、Oracle Databaseに作成されます。マテリアライズド・ビューは、問合せパフォーマンスの改善、レプリケートされたデータの提供などの多くの役割で使用できます。

以前は、サマリーを使用する場合、手動でのサマリーの作成、どのサマリーを作成するかの識別、サマリーへの索引付け、サマリーの更新、およびユーザーに対するサマリーのアドバイスに膨大な時間と労力を費やしていました。サマリー管理が導入されたことで、データベース管理者の作業負荷が軽減され、ユーザーはどのサマリーが定義されているかを把握する必要がなくなっています。データベース管理者は、サマリーと同じ結果を格納するマテリアライズド・ビューを1つ以上作成します。エンド・ユーザーは、表やビューをディテール・データ・レベルで問い合せます。SQL問合せは、Oracleサーバーのクエリー・リライト・メカニズムにより、サマリー・テーブルを使用するように自動的にリライトされます。このメカニズムにより、問合せから結果を戻すための応答時間が短縮されます。データ・ウェアハウス内のマテリアライズド・ビューは、エンド・ユーザーやデータベース・アプリケーションに対して透過的です。

マテリアライズド・ビューは、通常、クエリー・リライト機能を使用してアクセスされますが、エンド・ユーザーまたはデータベース・アプリケーションは、マテリアライズド・ビューに直接アクセスする問合せも作成できます。ただし、マテリアライズド・ビューに変更があるとそれを参照する問合せに影響するため、ユーザーにこれを許可するかどうかについては慎重に検討する必要があります。

データ・ウェアハウスでのマテリアライズド・ビュー

データ・ウェアハウスでは、マテリアライズド・ビューを使用して、売上合計などの集計データを事前に計算し格納できます。これらの環境では、マテリアライズド・ビューにサマリー・データが格納されるため、マテリアライズド・ビューは、サマリーとして参照されます。また、マテリアライズド・ビューを使用して、集計の有無にかかわらず、結合を事前に計算できます。マテリアライズド・ビューによって、大規模または重要な問合せの、コストの高い結合や集計によって発生するオーバーヘッドを回避できます。

分散コンピューティングでのマテリアライズド・ビュー

分散環境では、マテリアライズド・ビューを使用して、分散サイトでデータをレプリケートし、各サイトで競合解消方法を使用して実行された更新を同期できます。複製としてのマテリアライズド・ビューによって、本来はリモート・サイトからアクセスする必要があるデータに、ローカルにアクセスできます。マテリアライズド・ビューは、リモート・データ・マートでも有効です。分散コンピューティングとモバイル・コンピューティングの詳細は、『Oracle Databaseアドバンスト・レプリケーション』および『Oracle Database Heterogeneous Connectivity管理者ガイド』を参照してください。

モバイル・コンピューティングでのマテリアライズド・ビュー

マテリアライズド・ビューを使用して、クライアントとセントラル・サーバー間で定期的にリフレッシュおよび更新を行い、データのサブセットをセントラル・サーバーからモバイル・クライアントにダウンロードすることもできます。

この章では、データ・ウェアハウスでのマテリアライズド・ビューの使用方法を中心に説明しています。分散コンピューティングとモバイル・コンピューティングの詳細は、『Oracle Databaseアドバンスト・レプリケーション』および『Oracle Database Heterogeneous Connectivity管理者ガイド』を参照してください。

マテリアライズド・ビューの必要性

マテリアライズド・ビューを使用すると、非常に大規模なデータベースに対する問合せの速度が上がります。大規模データベースへの問合せには、多くの場合、表間の結合またはSUMなどの集計(あるいはその両方)が伴います。これらの操作は、時間および処理能力の点でコストが高くなります。作成するマテリアライズド・ビューのタイプによって、マテリアライズド・ビューのリフレッシュ方法およびクエリー・リライトによる使用方法が決まります。

マテリアライズド・ビューは、問合せを実行する前にコストの高い結合および集計操作をデータベース上で事前に計算し、その結果をデータベースに格納することで、問合せのパフォーマンスを改善します。問合せオプティマイザでは、問合せの要求を満たすのに既存のマテリアライズド・ビューが使用可能かどうか、また必要かどうかが自動的に認識されます。そして、使用可能であれば問合せオプティマイザは、マテリアライズド・ビューを使用するように、問合せを透過的にリライトします。その結果問合せは、ベースとなるディテール表ではなく、マテリアライズド・ビューに対して直接実行されます。一般に、ディテール表ではなくマテリアライズド・ビューを使用するように問合せをリライトすると、応答のパフォーマンスが改善されます。クエリー・リライトがどのように機能するかを図9-1に示します。

図9-1 透過的なクエリー・リライト

クエリー・リライトを使用する場合は、できるだけ多くの問合せを満たすマテリアライズド・ビューを作成します。たとえば、ディテール表またはファクト表に共通して適用される20の問合せがわかっている場合、適切に作成されたマテリアライズド・ビューが5、6個あればこれらの問合せを満たせる場合があります。マテリアライズド・ビュー定義には、任意の数の集計(SUM、COUNT(x)、COUNT(*)、COUNT(DISTINCT x)、AVG、VARIANCE、STDDEV、MINおよびMAX)を含めることができます。また、任意の数の結合を含めることもできます。どのようなマテリアライズド・ビューを作成する必要があるかわからない場合のために、OracleにはSQLアクセス・アドバイザが用意されています。これは、DBMS_ADVISORパッケージに含まれる一連のアドバイザ・プロシージャで、クエリー・リライトのためにマテリアライズド・ビューを設計および評価する場合に有効です。

マテリアライズド・ビューがクエリー・リライトによって使用される場合、そのマテリアライズド・ビューは、基になるディテール表と同じデータベースに格納する必要があります。マテリアライズド・ビューはパーティション化することも、パーティション表にマテリアライズド・ビューを定義することも可能です。また、マテリアライズド・ビューに1つ以上の索引を定義することもできます。

索引とは異なり、SELECT文を使用してマテリアライズド・ビューに直接アクセスできます。ただし、アプリケーションに影響を与えずにSQL文を変更することが難しくなるので、直接的にマテリアライズド・ビューを参照するSQL文は使用しないようにすることをお薦めします。かわりに、問合せがマテリアライズド・ビューを使用するように、クエリー・リライトで透過的にリライトすることをお薦めします。

この章で説明しているのは、データ・ウェアハウスでマテリアライズド・ビューを使用する方法です。マテリアライズド・ビューは、Oracle Replicationでも使用できます。詳細は、『Oracle Databaseアドバンスト・レプリケーション』を参照してください。

サマリー管理のコンポーネント

サマリー管理は、次のもので構成されます。

- マテリアライズド・ビューおよびディメンションの定義機能。

- すべてのマテリアライズド・ビューに最新データが確実に含まれるようにするリフレッシュ機能。

- マテリアライズド・ビューを使用するために問合せを透過的にリライトするクエリー・リライト機能。

- 作成するべきマテリアライズド・ビュー、パーティションおよび索引を推奨するSQLアクセス・アドバイザ。

- マテリアライズド・ビューを高速リフレッシュ可能にする方法、および一般的なクエリー・リライトの使用方法を提示するTUNE_MVIEW。

サマリー管理機能を使用する際に、スキーマに関する制限事項はありません。また、この機能を使用すると、データベースまたはアプリケーションを再設計しなくても、いくつかの既存DSSデータベース・アプリケーションのパフォーマンスを改善できます。

図9-2に、ウェアハウス・サイクルでのサマリー管理の使用例を示します。データがウェアハウスのディテール・データに変換、ステージングおよびロードされると、サマリー管理プロセスを起動できます。まず、SQLアクセス・アドバイザを使用して、マテリアライズド・ビューをどのように使用するかを計画します。次に、マテリアライズド・ビューを作成し、クエリー・リライトの方法を設計します。マテリアライズド・ビューの動作に問題がある場合は、TUNE_MVIEWを使用すると、マテリアライズド・ビューを最適化できます。

図9-2 サマリー管理の概要

データ・ウェアハウス設計の初期の段階でサマリー管理プロセスを理解しておくと、後で、パフォーマンスの向上、サマリー管理コストの削減および必要な記憶域の削減という大きなメリットを得ることができます。

データ・ウェアハウスの用語

データ・ウェアハウスの基本的な用語の定義は、次のとおりです。

- ディメンション表とは、企業のビジネス・エンティティを表します。通常、時間、部門、場所、製品などの階層およびカテゴリ情報として表されます。ディメンション表は、参照表とも呼ばれます。
ディメンション表は、通常、時間をかけてゆっくり変化し、定期的に変更されることはありません。長時間実行される意思決定支援問合せで、問合せから戻されるデータをディメンション階層の該当レベルに集計するために使用されます。

- 階層には、データベースでのビジネスの関係と共通のアクセス・パターンが記述されます。典型的な作業負荷を理解し、ディメンションの分析をすることで、マテリアライズド・ビューを作成できます。詳細は、第11章「ディメンション」を参照してください。

- ファクト表とは、企業のビジネス・トランザクションを表します。
データ・ウェアハウスのほとんどのデータは、少数の非常に大きなファクト表に格納されます。これらの表は、1つ以上の業務系OLTPデータベースからのデータで定期的に更新されます。

ファクト表には、売上、個数、在庫などのファクト(メジャーとも呼ぶ)が含まれます。

- 単純メジャーは、1つの表の数値または文字の列(fact.salesなど)です。

- 計算済メジャーは、1つの表のメジャーを含む式(fact.revenues - fact.expensesなど)です。

- マルチ表メジャーは、複数の表で定義される計算済メジャー(fact_a.revenues - fact_b.expensesなど)です。

ファクト表には、時間、製品、市場など、関連するビジネス・エンティティごとにビジネス・トランザクションを編成する1つ以上の外部キーが含まれます。ほとんどの場合、これらの外部キーはNULLでなく、ファクト表の複合一意キーを形成します。外部キーはそれぞれ ディメンション表の1つの行のみと結合します。

- マテリアライズド・ビューは事前に計算された表で、ファクト表および場合によってはディメンション表からの集計データおよび結合データで構成されます。データ・ウェアハウスの作成者の間では、マテリアライズド・ビューはサマリーとも呼ばれます。

マテリアライズド・ビューのスキーマ・デザイン

データ・ウェアハウス・デザインがこれらのガイドラインに従わない場合でも、サマリー管理によって、クエリー・リライトおよびマテリアライズド・ビューのリフレッシュを含む多くの有効な機能が実行できます。ただし、スキーマ・デザインがこれらのガイドラインに従う場合は、問合せ実行パフォーマンスおよびマテリアライズド・ビューのリフレッシュ・パフォーマンスが大幅に向上し、必要なマテリアライズド・ビューの数を削減できます。

マテリアライズド・ビューの定義には、任意の数の集計および結合を含めることができます。いくつかの点で、マテリアライズド・ビューは索引と同じように動作します。

- マテリアライズド・ビューの目的は、問合せ実行パフォーマンスを向上させることです。

- マテリアライズド・ビューの存在は、SQLアプリケーションに対して透過的であるため、データベース管理者は、SQLアプリケーションの妥当性に影響を与えることなく、いつでもマテリアライズド・ビューを作成または削除できます。

- マテリアライズド・ビューは、記憶領域を消費します。

- マテリアライズド・ビューの内容は、ベースとなるディテール表が変更された場合に、更新される必要があります。

スキーマとディメンション表

正規化または部分的に正規化されているディメンション表(複数の表に格納されているディメンション)の場合は、これらの表がどのように結合されているかを識別します。ディメンション表同士の結合においては、親表の各行と子表の各行の間に1対多の関係が保証されているかどうかに注意してください。また非正規化ディメンションの場合、子の列は親(または属性)の列を一意に決定できなくてはなりません。これらの制約で表されている関係が他の方法で保証されている場合、NOVALIDATEオプションおよびRELYオプションで使用可能にできます。ファクト表とディメンション表の間の結合がこの親子関係をサポートしない場合でも、CREATE DIMENSION文でディメンションを定義することで、パフォーマンスが大幅に向上します。制限を施行する別の方法には、マテリアライズド・ビューの定義(CREATE MATERIALIZED VIEW文)で外部結合を使用することがあります。

これらのリレーションシップを満たさないスキーマでは、ディメンションを作成しないでください。作成すると、問合せで不適切な結果が戻される場合があります。

マテリアライズド・ビューのスキーマ・デザイン・ガイドライン

サマリー管理の様々なコンポーネントを定義して使用する前に、スキーマ・デザインを調べて、できるだけ次のガイドラインに従う必要があります。

ガイドライン1および2は、ガイドライン3より重要です。スキーマ・デザインがガイドライン1および2に従っていない場合、ガイドライン3に従っているかどうかは問題ではありません。ガイドライン1、2および3は、クエリー・リライトのパフォーマンスおよびマテリアライズド・ビューのリフレッシュ・パフォーマンスの両方に影響します。

表9-1 スキーマ・デザイン・ガイドライン

スキーマ・ガイドライン

説明

ガイドライン1

ディメンション

(各ディメンションが1つの表に収まるように)ディメンションが非正規化されるか、正規化または部分的に正規化されたディメンションの表間の結合において、親表の各行と子表の各行の間に1対多の関係が保証される必要があります。この条件に従うメリットについては、「ディメンションの作成」を参照してください。

子の結合キーに外部キー制約およびNOT NULL制約を追加し、親の結合キーに主キー制約を追加すると、この条件を規定できます。

ガイドライン2

ディメンション

ディメンションが非正規化または部分的に非正規化されている場合、ディメンション表のキー列間で階層整合性を保つ必要があります。それぞれの子キー値は、ディメンション表が非正規化されていても、その親キー値を一意に識別する必要があります。非正規化ディメンションの階層整合性は、DBMS_DIMENSIONパッケージのVALIDATE_DIMENSIONプロシージャをコールすることで検証できます。

ガイドライン3

ディメンション

ファクト表およびディメンション表では、同様に、各ファクト表の行がディメンション表の1つの行のみと結合することを保証する必要があります。この条件を宣言し、オプションで規定する必要があります。それには、ファクト表のキー列に外部キー制約およびNOT NULL制約を追加し、ディメンション表のキー列に主キー制約を追加するか、外部結合を使用します。データ・ウェアハウスでは、制約規定によるパフォーマンス・オーバーヘッドを回避するために、通常、NOVALIDATE句およびRELY句を使用して制約を使用可能にします。詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

ガイドライン4

増分ロード

ディテール・データの増分ロードは、SQL*Loaderダイレクト・パス・オプション、またはOracleのダイレクト・パス・インタフェースを使用するバルク・ロード・ユーティリティを使用して実行する必要があります。これには、INSERT ... AS SELECT(APPENDまたはPARALLELヒントを利用)などがあり、このヒントによりダイレクト・ローダーのログが挿入時に使用されます。詳細は、『Oracle Database SQL言語リファレンス』および「マテリアライズド・ビューのタイプ」を参照してください。

ガイドライン5

パーティション

可能な場合は、単調に増加する(できればDATE型の)時間列によって、表のレンジ・パーティション化/コンポジット・パーティション化を行います。

ガイドライン6

ディメンション

各ロード後、マテリアライズド・ビューをリフレッシュする前に、DBMS_DIMENSIONパッケージのVALIDATE_DIMENSIONプロシージャを使用してディメンションの整合性を増分検証します。

ガイドライン7

時間ディメンション

時間ディメンションがマテリアライズド・ビューに時間列として表示される場合は、ファクト表の場合と同じ方法でマテリアライズド・ビューをパーティション化して索引を付けます。

制約を使用可能にするために必要な時間、および制約に違反する場合を考慮する必要がある場合は、ENABLE NOVALIDATE文でRELY句を使用して、既存の制約の妥当性チェックを行わずに、制約チェックをONにします。この方法には、制約が1つでも損われた場合、不正確な問合せ結果が戻される可能性があるというデメリットがあります。そのため、データがどれだけ正確か、また、不正確な結果が戻される可能性が大きすぎないかどうかを設計時に判断する必要があります。

データ・ウェアハウスへのデータのロード

データ・ウェアハウスまたはデータ・マートにデータをロードする一般的で効率的な方法には、SQL*LoaderでDIRECTまたはPARALLELオプションを使用する方法、データ・ポンプを使用する方法、またはOracleのダイレクト・パスAPIを使用する別のローダー・ツールを使用する方法があります。DIRECTまたはPARALLELキーワードを指定してSQL*Loaderを使用する場合の制限および考慮点については、『Oracle Databaseユーティリティ』を参照してください。

ロード方法は、1フェーズまたは2フェーズに分類できます。1フェーズ・ロードでは、データはターゲット表に直接ロードされ、品質保証テストが実行されます。エラーは、マテリアライズド・ビューをリフレッシュする前にDML操作を実行することによって解決されます。多くの削除が行われる場合、ディスク使用率に悪影響を及ぼすことがありますが、一時領域要件およびロード時間が最小化されます。

2フェーズ・ロード・プロセスでは、次の処理が行われます。

- データがウェアハウスの一時表にロードされます。

- 品質保証プロシージャがデータに適用されます。

- ターゲット表に対する参照整合性制約が使用禁止となり、ターゲット・パーティションのローカル索引がUNUSABLEとマークされます。

- INSERT AS SELECTを使用し、PARALLELまたはAPPENDヒントを指定することによって、データが一時領域からターゲット表の適切なパーティションにコピーされます。その後、一時表が削除されます。あるいは、ターゲット表がパーティション化されている場合は、ターゲット表に新しい(空の)パーティションを作成し、ALTER TABLE ... EXCHANGE PARTITIONを使用して一時表をターゲット表に取り込むこともできます。詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

- 通常、NOVALIDATEオプション付きで制約が使用可能になります。

ディテール・データのロードおよびディテール・データ上の索引の更新を行うと、必要に応じて、データベースに対する操作ができるようになります。ALTER SYSTEM SET QUERY_REWRITE_ENABLED = FALSE文を発行することにより、すべてのマテリアライズド・ビューがリフレッシュされるまで、クエリー・リライトをシステム・レベルで使用禁止にできます。

QUERY_REWRITE_INTEGRITYをSTALE_TOLERATEDと設定した場合、ALTER SESSION SET QUERY_REWRITE_ENABLED = TRUE文を発行すると、最新ロードのデータが反映されたマテリアライズド・ビューを必要としないユーザーに対して、マテリアライズド・ビューへのアクセスをセッション・レベルで許可できます。この使用例は、QUERY_REWRITE_INTEGRITYがENFORCEDまたはTRUSTEDの場合には適用されません。これは、この2つのモードでは、更新されたデータを持つマテリアライズド・ビューのみがクエリー・リライトで使用されるためです。

マテリアライズド・ビューの管理作業の概要

マテリアライズド・ビューの使用目的はパフォーマンスの向上ですが、マテリアライズド・ビュー管理によるオーバーヘッドが、システム管理上の大きな問題になる場合があります。マテリアライズド・ビューに必要な管理アクティビティをレビューまたは評価するときは、次のことを考慮します。

- 最初に作成するマテリアライズド・ビューの特定

- マテリアライズド・ビューの索引付け

- データベース更新のたびに、すべてのマテリアライズド・ビューおよびその索引が適切にリフレッシュされたかどうかの確認

- 使用されたマテリアライズド・ビューのチェック

- 作業負荷パフォーマンスに対する各マテリアライズド・ビューの効率の判断

- マテリアライズド・ビューが使用した領域の計算

- 作成が必要な新しいマテリアライズド・ビューの判断

- 削除が必要な既存のマテリアライズド・ビューの判断

- 有効ではなくなった古いディテールおよびマテリアライズド・ビュー・データのアーカイブ

データ・ウェアハウスまたはデータ・マートを最初に作成および移入した後の主な管理オーバーヘッドは更新処理です。これには次が含まれます。

- 業務系システムによる増分変更の定期的な抽出

- データの変換

- 増分変更が正しく、一貫性があり、完全であるかどうかの検証

- ウェアハウスへのデータのバルク・ロード

- ディテール・データに対する一貫性を保つための索引とマテリアライズド・ビューのリフレッシュ

通常、更新処理は、更新ウィンドウと呼ばれる制限時間内に実行される必要があります。更新ウィンドウは、更新頻度(毎日、毎週など)およびビジネスの特性によって異なります。更新頻度が毎日の場合、更新ウィンドウは2から6時間になります。

次のアクティビティの更新ウィンドウを知る必要があります。

- ディテール・データのロード

- ディテール・データに対する索引の更新または再作成

- データに対する品質保証テストの実行

- マテリアライズド・ビューのリフレッシュ

- マテリアライズド・ビューに対する索引の更新

マテリアライズド・ビューのタイプ

マテリアライズド・ビュー作成文のSELECT句で、マテリアライズド・ビューに含めるデータが定義されます。指定できる内容に関する制限はごく少数です。必要な数の表を結合できます。表のみでなく、ビュー、インライン・ビュー(SELECT文のFROM句の副問合せ)、副問合せおよびマテリアライズド・ビューなど、他の要素もすべてSELECT句で結合または参照できます。ただし、定義問合せのSELECT構文のリストに副問合せを持つマテリアライズド・ビューは定義できません。WHERE句など、定義問合せのその他の場所に副問合せを記述することは可能です。

マテリアライズド・ビューには、次のタイプがあります。

- 集計を含むマテリアライズド・ビュー

- 結合のみを含むマテリアライズド・ビュー

- ネステッド・マテリアライズド・ビュー

集計を含むマテリアライズド・ビュー

データ・ウェアハウスでは、通常、マテリアライズド・ビューには例9-1のような集計が含まれています。高速リフレッシュを可能にするには、SELECT構文のリストにすべてのGROUP BY列(ある場合)を含める必要があります。また、集計列にCOUNT(*)およびCOUNT(column)が必要です。さらに、マテリアライズド・ビュー・ログは、マテリアライズド・ビューを定義する問合せで参照されるすべての表に関して存在する必要があります。有効な集計関数は、SUM、COUNT(x)、COUNT(*)、AVG、VARIANCE、STDDEV、MIN、MAXです。また、任意のSQL値式を集計できます。「集計を含むマテリアライズド・ビューの高速リフレッシュに関する制限」を参照してください。

結合と集計を含むマテリアライズド・ビューの高速リフレッシュは、実表に対してDML(ダイレクト・ロードまたは従来型のINSERT、UPDATEまたはDELETE)を実行した後に可能になります。ON COMMITまたはON DEMANDでリフレッシュされるように定義できます。REFRESH ON COMMITを指定した場合は、マテリアライズド・ビューのディテール表の1つに対してDMLを実行するトランザクションがコミットされると、マテリアライズド・ビューが自動的にリフレッシュされます。この方法を選択すると、コミット完了までの所要時間は通常より少し長くなることがあります。これは、リフレッシュ操作がコミット・プロセスの一部として実行されるためです。したがって、この方法は、多数のユーザーがマテリアライズド・ビューの実表を同時に変更する場合には適していません。

次に、集計を含むマテリアライズド・ビューの例をいくつか示します。この例のマテリアライズド・ビューは高速リフレッシュされるため、マテリアライズド・ビュー・ログの作成は必須であることに注意してください。

例9-1 例1: マテリアライズド・ビューの作成

CREATE MATERIALIZED VIEW LOG ON products WITH SEQUENCE, ROWID
(prod_id, prod_name, prod_desc, prod_subcategory, prod_subcategory_desc,
prod_category, prod_category_desc, prod_weight_class, prod_unit_of_measure,
prod_pack_size, supplier_id, prod_status, prod_list_price, prod_min_price)
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON sales
WITH SEQUENCE, ROWID
(prod_id, cust_id, time_id, channel_id, promo_id, quantity_sold, amount_sold)
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW product_sales_mv
PCTFREE 0  TABLESPACE demo
STORAGE (INITIAL 8M)
BUILD IMMEDIATE
REFRESH FAST
ENABLE QUERY REWRITE
AS SELECT p.prod_name, SUM(s.amount_sold) AS dollar_sales,
COUNT(*) AS cnt, COUNT(s.amount_sold) AS cnt_amt
FROM sales s, products p
WHERE s.prod_id = p.prod_id GROUP BY p.prod_name;

この例では、製品の合計売上数と合計売上金額を計算するマテリアライズド・ビューproduct_sales_mvが作成されます。これは、prod_id列で表salesおよびproductsを結合することで導出されます。このマテリアライズド・ビューは、作成方法がIMMEDIATEであるため、データがすぐに移入され、クエリー・リライトに使用できます。この例では、デフォルトのリフレッシュ方法はFASTです。これが許されるのは、表productsおよびsalesに関して適切なマテリアライズド・ビュー・ログが作成されているためです。

WITH COMMIT SCN句を含むマテリアライズド・ビュー・ログを使用すると、ローカル・マテリアライズド・ビューの高速リフレッシュのパフォーマンスが向上します。次に例を示します。

CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID(prod_id, cust_id, time_id),
COMMIT SCN INCLUDING NEW VALUES;

例9-2 例2: マテリアライズド・ビューの作成

CREATE MATERIALIZED VIEW product_sales_mv
PCTFREE 0 TABLESPACE demo
STORAGE (INITIAL 8M)
BUILD DEFERRED
REFRESH COMPLETE ON DEMAND
ENABLE QUERY REWRITE AS
SELECT p.prod_name, SUM(s.amount_sold) AS dollar_sales
FROM sales s, products p WHERE s.prod_id = p.prod_id
GROUP BY p.prod_name;

この例では、prod_nameごとの合計売上を計算するマテリアライズド・ビューproduct_sales_mvが作成されます。これは、prod_id列で表salesおよびproductsを結合することで導出されます。このマテリアライズド・ビューは、作成方法がDEFERREDであるため、最初は、どのデータも含みません。作成方法がDEFERREDのマテリアライズド・ビューの最初のリフレッシュには、完全リフレッシュが必要です。このマテリアライズド・ビューがリフレッシュされ、移入されると、クエリー・リライトに使用できます。

例9-3 例3: マテリアライズド・ビューの作成

CREATE MATERIALIZED VIEW LOG ON sales WITH SEQUENCE, ROWID
(prod_id, cust_id, time_id, channel_id, promo_id, quantity_sold, amount_sold)
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW sum_sales
PARALLEL
BUILD IMMEDIATE
REFRESH FAST ON COMMIT AS
SELECT s.prod_id, s.time_id, COUNT(*) AS count_grp,
SUM(s.amount_sold) AS sum_dollar_sales,
COUNT(s.amount_sold) AS count_dollar_sales,
SUM(s.quantity_sold) AS sum_quantity_sales,
COUNT(s.quantity_sold) AS count_quantity_sales
FROM sales s
GROUP BY s.prod_id, s.time_id;

この例では、単一表集計を含むマテリアライズド・ビューが作成されます。マテリアライズド・ビューの定義問合せの中ですべての参照列にマテリアライズド・ビュー・ログが作成されているので、このマテリアライズド・ビューは高速リフレッシュが可能です。DMLがsales表に対して適用される場合、コミットが発行されたときに変更がマテリアライズド・ビューに反映されます。

集計を含むマテリアライズド・ビューの使用要件

表9-2に、マテリアライズド・ビューの集計要件を示します。集計Xがある場合、集計Yが必要であり、集計Zはオプションです。

表9-2 集計を含むマテリアライズド・ビューの要件

X

Y

Z

COUNT(expr)

-

-

MIN(expr)

-

-

MAX(expr)

-

-

SUM(expr)

COUNT(expr)

-

SUM(col)(colにはNOT NULL制約あり)

-

-

AVG(expr)

COUNT(expr)

SUM(expr)

STDDEV(expr)

COUNT(expr) SUM(expr)

SUM(expr * expr)

VARIANCE(expr)

COUNT(expr) SUM(expr)

SUM(expr * expr)

すべてのタイプの高速リフレッシュを保証するには、常にCOUNT(*)が必要です。さもないと、挿入後の高速リフレッシュのみに制限される場合があります。集計を最も効率よく正確に高速リフレッシュできるように、マテリアライズド・ビューにZ列のオプションの集計も含めることをお薦めします。

結合のみを含むマテリアライズド・ビュー

例9-4のように、マテリアライズド・ビューに結合のみが含まれ、集計は含まれない場合があります。この例では、sales表をtimes表とcustomers表に結合するマテリアライズド・ビューが作成されます。このタイプのマテリアライズド・ビューを作成するメリットは、コストの高い結合が事前に計算されることです。

結合のみを含むマテリアライズド・ビューの高速リフレッシュは、実表に対してDML(ダイレクト・パスまたは従来型のINSERT、UPDATEまたはDELETE)を実行した後に可能になります。

結合のみを含むマテリアライズド・ビューは、ON COMMITまたはON DEMANDでリフレッシュされるように定義できます。ON COMMITの場合、リフレッシュは、マテリアライズド・ビューにある1つのディテール表上でDMLを実行するトランザクションのコミット時に実行されます。

REFRESH FASTを指定する場合、Oracleは、問合せ定義をさらに検証して、いずれかのディテール表が変更された場合の高速リフレッシュの実行を保証します。これらの追加チェックには、次の制限が含まれます。

- 表がPCTをサポートしていない場合、マテリアライズド・ビュー・ログがそれぞれのディテール表に対して作成されていること。また、マテリアライズド・ビュー・ログが必須の場合は、ROWID列が各マテリアライズド・ビュー・ログに存在していること。

- すべてのディテール表のROWIDが、マテリアライズド・ビュー問合せ定義のSELECT構文のリストにあること。

これらの制限で満たされないものがある場合は、マテリアライズド・ビューをREFRESH FORCEとして作成し、可能なときに高速リフレッシュの効果を得ることができます。表の1つがすべての基準を満たさなくても、他の表がすべての基準を満たしている場合は、すべての基準が満たされている他の表に関しては、マテリアライズド・ビューを高速リフレッシュできます。

最も効率的にリフレッシュが実行されるようにするには、内部結合のように動作する外部結合を定義問合せで使用しないようにします。このような結合が定義問合せに含まれている場合は、内部結合を使用するように定義問合せをリライトすることを検討してください。リフレッシュ・パフォーマンスを低下させる条件の詳細は、「結合のみを含むマテリアライズド・ビューの高速リフレッシュに関する制限」を参照してください。

マテリアライズド結合ビューのFROM句に関する考慮事項

マテリアライズド・ビューに含まれているのが結合のみである場合は、各表のROWID列(およびFROM句のリストに複数回指定されている表)を、マテリアライズド・ビューのSELECT構文のリストに指定する必要があります。

マテリアライズド・ビューのFROM句にリモート表が指定されている場合、そのFROM句の表はすべて同じサイトに配置されている必要があります。また、リモート表を含むマテリアライズド・ビューでは、ON COMMITによるリフレッシュはサポートされません。SCNベースのマテリアライズド・ビュー・ログを除き、マテリアライズド・ビューの各ディテール表のマテリアライズド・ビュー・ログはリモート・サイトに作成されている必要があり、ROWID列は、次の例に示すようにマテリアライズド・ビューのSELECT構文のリストに指定されている必要があります。

例9-4 結合のみを含むマテリアライズド・ビュー

CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON times WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON customers WITH ROWID;
CREATE MATERIALIZED VIEW detail_sales_mv
PARALLEL BUILD IMMEDIATE
REFRESH FAST AS
SELECT s.rowid "sales_rid", t.rowid "times_rid", c.rowid "customers_rid",
c.cust_id, c.cust_last_name, s.amount_sold, s.quantity_sold, s.time_id
FROM sales s, times t, customers c
WHERE  s.cust_id = c.cust_id(+) AND s.time_id = t.time_id(+);

また、前述の例にtimes_rid列およびcustomers_rid列が含まれず、リフレッシュ方法がREFRESH FORCEであった場合、このマテリアライズド・ビューを高速リフレッシュできるのは、sales表が更新された場合のみです。times表またはcustomers表が更新された場合は、高速リフレッシュできません。

CREATE MATERIALIZED VIEW detail_sales_mv
PARALLEL
BUILD IMMEDIATE
REFRESH FORCE AS
SELECT s.rowid "sales_rid", c.cust_id, c.cust_last_name, s.amount_sold,
s.quantity_sold, s.time_id
FROM sales s, times t, customers c
WHERE s.cust_id = c.cust_id(+) AND s.time_id = t.time_id(+);

ネステッド・マテリアライズド・ビュー

ネステッド・マテリアライズド・ビューとは、その定義が別のマテリアライズド・ビューに基づいているマテリアライズド・ビューです。ネステッド・マテリアライズド・ビューは、マテリアライズド・ビューの他に、データベース内の他のリレーションも参照する場合があります。

ネステッド・マテリアライズド・ビューを使用する理由

データ・ウェアハウスでは、通常、単一の結合上に多数の集計ビュー(たとえば、異なるディメンションに沿ったロールアップ)を作成します。これらの個別の結合と集計を含むマテリアライズド・ビューに対する増分メンテナンスは、ベースとなる結合が何度も実行される必要があるため、かなりの時間がかかります。

ネステッド・マテリアライズド・ビューを使用すると、結合のみを含む1つのマテリアライズド・ビューに基づいて複数の単一表マテリアライズド・ビューを作成できます。さらに、この種類の単一表集計マテリアライズド・ビューには最適化が実行され、リフレッシュが非常に効率的になります。

例9-5 ネステッド・マテリアライズド・ビュー

マテリアライズド・ビューに対してネステッド・マテリアライズド・ビューを作成できますが、親およびベースのマテリアライズド・ビューには結合または集計が含まれている必要があります。マテリアライズド・ビューの定義問合せに結合または集計が含まれない場合はネストできません。マテリアライズド・ビューの定義のベースとなるすべてのオブジェクト(マテリアライズド・ビューまたは表)には、マテリアライズド・ビュー・ログが必要です。ベースとなるオブジェクトは、すべて表と同様に扱われます。また、マテリアライズド・ビュー用のオプションはすべて使用できます。

次のマテリアライズド・ビューは、shサンプル・スキーマの表と列を使用するネステッド・マテリアライズド・ビューの作成方法を示しています。

CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON customers WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON times WITH ROWID;

/*create materialized view join_sales_cust_time as fast refreshable at
COMMIT time */
CREATE MATERIALIZED VIEW join_sales_cust_time
REFRESH FAST ON COMMIT AS
SELECT c.cust_id, c.cust_last_name, s.amount_sold, t.time_id,
t.day_number_in_week, s.rowid srid, t.rowid trid, c.rowid crid
FROM sales s, customers c, times t
WHERE s.time_id = t.time_id AND s.cust_id = c.cust_id;

join_sales_cust_time表に対するネステッド・マテリアライズド・ビューを作成するには、その表に対してマテリアライズド・ビュー・ログを作成する必要があります。これはjoin_sales_cust_time表に対する単一表集計マテリアライズド・ビューになるため、必要なすべての列をログに記録して、INCLUDING NEW VALUES句を使用する必要があります。

/* create materialized view log on join_sales_cust_time */
CREATE MATERIALIZED VIEW LOG ON join_sales_cust_time
WITH ROWID (cust_last_name, day_number_in_week, amount_sold),
INCLUDING NEW VALUES;

/* create the single-table aggregate materialized view sum_sales_cust_time
on join_sales_cust_time as fast refreshable at COMMIT time */
CREATE MATERIALIZED VIEW sum_sales_cust_time
REFRESH FAST ON COMMIT AS
SELECT COUNT(*) cnt_all, SUM(amount_sold) sum_sales, COUNT(amount_sold)
cnt_sales, cust_last_name, day_number_in_week
FROM join_sales_cust_time
GROUP BY cust_last_name, day_number_in_week;

結合および集計を含むマテリアライズド・ビューのネスト

ネステッド・マテリアライズド・ビューの中には、高速リフレッシュできないタイプがあります。このようなタイプのマテリアライズド・ビューを特定するには、EXPLAIN_MVIEWを使用します。DBMS_MVIEW.REFRESHパラメータとともにnested = TRUEパラメータを指定すると、適切な依存順序でネステッド・マテリアライズド・ビューのツリーをリフレッシュできます。たとえば、DBMS_MVIEW.REFRESH ('SUM_SALES_CUST_TIME', nested => TRUE)とコールすると、REFRESHプロシージャによって、まずjoin_sales_cust_timeマテリアライズド・ビューがリフレッシュされ、次にsum_sales_cust_timeマテリアライズド・ビューがリフレッシュされます。

ネステッド・マテリアライズド・ビューの使用上のガイドライン

ネステッド・マテリアライズド・ビューを使用するかどうかを決定する場合、次の点に注意する必要があります。

- 高速リフレッシュが必要な場合は、依存しているすべてのマテリアライズド・ビューも高速リフレッシュする必要があります。

- 最高レベルのマテリアライズド・ビューをディテール表と同じ更新レベルに保つには、最高レベルのマテリアライズド・ビューをリフレッシュする前に、ツリー内のすべてのマテリアライズド・ビューが正しい依存順序でリフレッシュされるようにする必要があります。「結合および集計を含むマテリアライズド・ビューのネスト」で説明しているように、nested = TRUEパラメータを使用すると、ネスト階層内の中間マテリアライズド・ビューを自動的にリフレッシュできます。nested = TRUEを指定せず、最高レベルのマテリアライズド・ビューの下にあるマテリアライズド・ビューが失効している場合、最高レベルのみのリフレッシュは正常に終了しますが、これはベースとなるマテリアライズド・ビューの観点から更新されているのみで、ツリーの基礎であるディテール表の観点から更新されているわけではありません。

- マテリアライズド・ビューをリフレッシュするときは、ツリー内のすべてのマテリアライズド・ビューがリフレッシュされるようにする必要があります。最高レベルのマテリアライズド・ビューのみをリフレッシュした場合、その下にあるマテリアライズド・ビューは失効するため、これらは明示的にリフレッシュする必要があります。nestedパラメータの値をTRUEに設定してREFRESHプロシージャを実行した場合、指定したマテリアライズド・ビューと、ツリー内の子マテリアライズド・ビューのみがリフレッシュされます。そのツリー内の最高レベルのマテリアライズド・ビューはリフレッシュされません。ツリー内のすべてのマテリアライズド・ビューを確実にリフレッシュする必要がある場合は、nestedパラメータの値をTRUEに設定してREFRESH_DEPENDENTプロシージャを実行します。

- マテリアライズド・ビューが最新であるかどうかは、そのマテリアライズド・ビューが直接的に参照しているオブジェクトとの比較によって決められます。マテリアライズド・ビューが別のマテリアライズド・ビューを参照している場合、最高レベルのマテリアライズド・ビューが最新であるかどうかは、そのマテリアライズド・ビューが直接的に参照しているマテリアライズド・ビューでの変更と比較することによって決められます。

ネステッド・マテリアライズド・ビューの使用上の制限

マテリアライズド・ビューと事前作成マテリアライズド・ビューの両方は同じ表に作成できません。たとえば、表costsとこの表に基づくマテリアライズド・ビューcost_mvがある場合、表costsに対して事前作成マテリアライズド・ビューは作成できません。作成すると、cost_mvがネステッド・マテリアライズド・ビューになります。このような変換方法はサポートされていません。

マテリアライズド・ビューの作成

マテリアライズド・ビューは、CREATE MATERIALIZED VIEW文またはEnterprise Managerを使用して作成できます。例9-6では、cust_sales_mvというマテリアライズド・ビューが作成されます。

例9-6 マテリアライズド・ビューの作成

CREATE MATERIALIZED VIEW cust_sales_mv
PCTFREE 0 TABLESPACE demo
STORAGE (INITIAL 8M)
PARALLEL
BUILD IMMEDIATE
REFRESH COMPLETE
ENABLE QUERY REWRITE AS
SELECT  c.cust_last_name, SUM(amount_sold) AS sum_amount_sold
FROM customers c, sales s WHERE s.cust_id = c.cust_id
GROUP BY c.cust_last_name;

一般的に、データ・ウェアハウスにはすでにサマリー表または集計表が作成されており、新しいマテリアライズド・ビューを作成して、この作業を繰り返すことはありません。この場合、すでにデータベースに存在する表は、事前作成マテリアライズド・ビューとして登録できます。この方法については、「既存のマテリアライズド・ビューの登録」を参照してください。

作成するマテリアライズド・ビューを選択した後、各マテリアライズド・ビューに対して次の手順を実行します。

1. マテリアライズド・ビューを設計します。既存のユーザー定義のマテリアライズド・ビューについてはこの手順は不要です。マテリアライズド・ビューに多数の行が含まれる場合、適切であれば、マテリアライズド・ビューをパーティション化します(可能な場合)。このパーティション化は、最大または最も頻繁に更新されるディテール表またはファクト表のパーティション化と一致させる必要があります(可能な場合)。パーティション化により、リフレッシュのパフォーマンスが向上します。これは、パラレルDML機能と、場合によってはPCTベースのリフレッシュを利用できるようになるためです。

2. CREATE MATERIALIZED VIEW文を使用して、マテリアライズド・ビューを作成および移入(オプション)します。すでにユーザー定義マテリアライズド・ビューが存在する場合は、CREATE MATERIALIZED VIEW文のON PREBUILT TABLE句を使用します。それ以外の場合は、BUILD IMMEDIATE句を使用してマテリアライズド・ビューにすぐに移入するか、BUILD DEFERRED句を使用して後から移入します。BUILD DEFERREDで作成したマテリアライズド・ビューは、最初のCOMPLETE REFRESHが行われるまで、クエリー・リライトで使用できません。ENABLE QUERY REWRITE句を指定していれば、最初のリフレッシュの後で自動的にクエリー・リライトが使用可能になります。

関連項目:
SQL文CREATE MATERIALIZED VIEW、ALTER MATERIALIZED VIEWおよびDROP MATERIALIZED VIEWの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

列の別名リストを含むマテリアライズド・ビューの作成

現在のバージョンでは、マテリアライズド・ビューの作成時、定義問合せのSELECT構文のリストに同じ名前の列が複数含まれている場合は、それらに一意の別名を付与して名前の競合を解消する必要があります。競合を解消しないと、列の定義が曖昧であるというエラー・メッセージが表示され、CREATE MATERIALIZED VIEW文が失敗します。しかし、名前解決のために通常の方法でSELECT句に別名を指定すると、テキストの完全一致のクエリー・リライトの使用が制限され、マテリアライズド・ビューの定義問合せのテキストとユーザーの入力問合せのテキストが同一である場合にのみ、テキストの完全一致のリライトが行われることになります。したがって、マテリアライズド・ビューの定義問合せのSELECT構文のリストに別名を指定し、問合せに別名が指定されなかった場合、テキストの完全一致の比較は失敗します。これは特に、列の別名を大量に使用するDiscovererからの問合せで問題になります。

次に、問題の例を示します。sales_mvを作成する際、SELECT句に列の別名が指定されていますが、入力問合せQ1には別名が指定されていません。このため、テキストの完全一致のリライトは失敗します。マテリアライズド・ビューは次のとおりです。

CREATE MATERIALIZED VIEW sales_mv
ENABLE QUERY REWRITE AS
SELECT s.time_id sales_tid, c.time_id costs_tid
FROM sales s, products p, costs c
WHERE s.prod_id = p.prod_id AND c.prod_id = p.prod_id AND
p.prod_name IN (SELECT prod_name FROM products);

入力問合せ文Q1は次のとおりです。

SELECT s.time_id, c1.time_id
FROM sales s, products p, costs c1
WHERE s.prod_id = p.prod_id AND c1.prod_id = p.prod_id AND
p.prod_name IN (SELECT prod_name FROM products);

マテリアライズド・ビューの定義問合せは、ユーザーの入力問合せとほとんど同一であり、論理的には同等ですが、クエリー・リライトは行われません。これは、一部の問合せ(WHERE句の副問合せなど)ではリライトが行われる唯一の可能性であるテキストの完全一致に失敗しているためです。

CREATE MATERIALIZED VIEW文には、列の別名リストを追加できます。列の別名リストを使用すると、マテリアライズド・ビューのSELECT句に別名を指定することなく、列名の競合を明示的に解消できます。次の例に、マテリアライズド・ビューの列の別名リストの構文を示します。

CREATE MATERIALIZED VIEW sales_mv (sales_tid, costs_tid)
ENABLE QUERY REWRITE AS
SELECT s.time_id, c.time_id
FROM sales s, products p, costs c
WHERE s.prod_id = p.prod_id AND c.prod_id = p.prod_id AND
p.prod_name IN (SELECT prod_name FROM products);

この例では、sales_mvの定義問合せが、ユーザー問合せQ1と完全に一致しています。このため、テキストの完全一致のリライトが行われます。

別名をSELECT句と新しい別名リスト句の両方に指定した場合は、SELECT句に指定した別名ではなく、別名リスト句が使用されます。

マテリアライズド・ビューの名前付け

マテリアライズド・ビューの名前は、Oracleの標準のネーミング規則に従っている必要があります。ただし、マテリアライズド・ビューがユーザー定義の事前作成表を基にしている場合は、マテリアライズド・ビューの名前はその表名と一致させる必要があります。

すでに表および索引のネーミング規則がある場合は、このネーミング計画をマテリアライズド・ビューに拡張して、マテリアライズド・ビューを簡単に識別できるようにすることが可能です。たとえば、マテリアライズド・ビューをsum_of_salesではなく、sum_of_sales_mvとネーミングすることで、これがマテリアライズド・ビューであり、表またはビューではないことを表すことができます。

記憶域および表の圧縮

マテリアライズド・ビューは、ユーザー定義の事前作成表を基にしていないかぎり、データベース内の記憶域を必要とし、これを占有します。したがって、マテリアライズド・ビューの記憶域が必要な場合、それが存在する表領域およびエクステントのサイズを指定する必要があります。

マテリアライズド・ビューに必要な領域のサイズがわからない場合は、DBMS_MVIEW.ESTIMATE_MVIEW_SIZEパッケージを使用して、圧縮されていないマテリアライズド・ビューの格納に必要なバイト数を見積ります。この情報は、設計者が、どの表領域にマテリアライズド・ビューを常駐させる必要があるかを判断する場合の参考になります。

表の圧縮は、冗長性の高いデータ(多数の外部キーを持つ表など)で使用します。これは、ROLLUP句を使用して作成したマテリアライズド・ビューには特に役立ちます。表の圧縮によりディスクの使用とメモリーの使用(具体的にはバッファ・キャッシュ)が削減され、読取り専用操作のスケールアップが向上します。また、表の圧縮によって、更新コストと引換えに問合せの実行速度が向上します。

表の圧縮は、このリリースのハイブリッド・コラム圧縮で拡張されています。特定のOracleストレージ・システムの機能であるハイブリッド・コラム圧縮では、データの格納に行と列の両方の方法を組み合せて使用します。データがロードされると、行のグループは列形式で格納され、指定した列の値が一緒に格納されて圧縮されます。同じデータ型や同様の特性の列データを一緒に格納すると、圧縮によってストレージの容量が大幅に節約されます。ハイブリッド・コラム圧縮では、複数のレベルの圧縮が提供されるため、更新アクティビティが最小限の表またはパーティションに最適です。

関連項目:

- 表の圧縮の詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

- 表の圧縮の詳細は、『Oracle Database管理者ガイド』を参照してください。

- STORAGEセマンティクスの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

- ハイブリッド・コラム圧縮の詳細は、『Oracle Database概要』を参照してください。

作成方法

表9-3に示すように、マテリアライズド・ビューを作成する方法は2つあります。BUILD IMMEDIATEで作成すると、マテリアライズド・ビューの定義がデータ・ディクショナリ内のスキーマ・オブジェクトに追加されます。その後、ファクト表またはディテール表がSELECT文に従ってスキャンされ、その結果がマテリアライズド・ビューに格納されます。スキャンされる表のサイズによっては、作成処理にかなりの時間がかかる場合があります。

BUILD DEFERRED句を使用することもできます。この句は、データなしでマテリアライズド・ビューを作成するため、第16章「データ・ウェアハウスのメンテナンス」で説明するDBMS_MVIEW.REFRESHパッケージを使用して、後でデータを移入できます。

表9-3 作成方法

作成方法

説明

BUILD IMMEDIATE

マテリアライズド・ビューを作成して、データを移入します。

BUILD DEFERRED

マテリアライズド・ビューの定義は作成しますが、データは移入しません。

クエリー・リライトの有効化

マテリアライズド・ビューを作成する前に、プロシージャDBMS_MVIEW.EXPLAIN_MVIEWをコールすると、使用可能なクエリー・リライトのタイプを確認できます。また、DBMS_ADVISOR.TUNE_MVIEWを使用すると、マテリアライズド・ビューを最適化して、多くのタイプのクエリー・リライトを使用可能にできます。マテリアライズド・ビューの作成後は、DBMS_MVIEW.EXPLAIN_REWRITEを使用して、特定の問合せがリライトされるかどうか(および、リライトされない理由)を調べることができます。

マテリアライズド・ビューが定義された場合でも、クエリー・リライト機能が自動的に使用されることはありません。クエリー・リライトがデフォルトで使用可能にされている場合でも、マテリアライズド・ビューをクエリー・リライトで使用可能にするには、ENABLE QUERY REWRITE句も指定する必要があります。

マテリアライズド・ビューの作成時に、この句を省略するか、DISABLE QUERY REWRITEとして指定した場合は、後でALTER MATERIALIZED VIEW文を使用して、マテリアライズド・ビューのクエリー・リライトを使用可能にできます。

マテリアライズド・ビューをBUILD DEFERREDとして定義する場合は、完全リフレッシュによってマテリアライズド・ビューにデータを移入しないかぎり、クエリー・リライトは使用できません。

クエリー・リライトの制限

すべてのマテリアライズド・ビューでクエリー・リライトが可能なわけではありません。クエリー・リライトが予想どおりに実行されない場合は、DBMS_MVIEW.EXPLAIN_REWRITEを使用すると、特定の問合せがリライトに適していない理由がわかります。このプロシージャによって、一部のクエリー・リライトが使用可能でないことが示された場合は、プロシージャDBMS_ADVISOR.TUNE_MVIEWを使用すると、マテリアライズド・ビューの定義を変更してクエリー・リライトを使用可能にできるかどうかを確認できます。また、マテリアライズド・ビューが次のすべての条件を満たしているかどうかをチェックしてください。

マテリアライズド・ビューの制限

次の制限を考慮する必要があります。

- マテリアライズド・ビューの定義問合せに、結果の再現が不可能な式(ROWNUM、SYSDATE、結果の再現が不可能なPL/SQLファンクションなど)を含めることはできません。

- 問合せにLONGまたはLONG RAWデータ型やREFオブジェクトの参照を含めることはできません。

- マテリアライズド・ビューがPREBUILTとして登録された場合、WITH REDUCED PRECISIONでオーバーライドされないかぎり、列の精度は対応するSELECT文の精度と一致する必要があります。

関連項目:

- 第19章「高度なクエリー・リライト」

- 『Oracle Database SQL言語リファレンス』

一般的なクエリー・リライトの制限

次の制限を考慮する必要があります。

- 問合せはローカル表およびリモート表の両方を参照する場合があります。このような問合せは、同じ表を参照している適格なマテリアライズド・ビューがローカルで使用できる場合にはリライトできます。

- SYSは、ディテール表もマテリアライズド・ビューも所有できません。

- マテリアライズド・ビューのGROUP BY句に列または式を指定する場合は、その列または式をSELECT構文のリストにも指定する必要があります。

- 集計関数は、式の最も外側でのみ使用する必要があります。つまり、AVG(AVG(x))やAVG(x)+ AVG(x)などの集計は実行できません。

- CONNECT BY句は使用できません。

関連項目:

- 第19章「高度なクエリー・リライト」

- 『Oracle Database SQL言語リファレンス』

リフレッシュ・オプション

マテリアライズド・ビューを定義する際は、リフレッシュ方法、リフレッシュのタイプ、トラステッド制約を使用可能にするかどうかの3つのリフレッシュ・オプションを指定できます。オプションを指定しない場合は、デフォルトとして、それぞれON DEMAND、FORCEおよびENFORCED制約が使用されます。

リフレッシュ実行モードは、ON COMMITおよびON DEMANDの2つです。作成するマテリアライズド・ビューによっては、一部のオプションを使用できない場合があります。表9-4にリフレッシュ・モードを示します。

表9-4 リフレッシュ・モード

リフレッシュ・モード

説明

ON COMMIT

マテリアライズド・ビューのディテール表の1つを変更したトランザクションをコミットした場合、リフレッシュが自動的に実行されます。このモードを使用できるのは、マテリアライズド・ビューが高速リフレッシュ可能な場合(つまり複雑でない場合)のみです。このモードを使用するには、ON COMMIT権限が必要です。

ON DEMAND

ユーザーがDBMS_MVIEWパッケージに含まれている使用可能なリフレッシュ・プロシージャ(REFRESH、REFRESH_ALL_MVIEWS、REFRESH_DEPENDENT)の1つを手動で実行した場合、リフレッシュが実行されます。

マテリアライズド・ビューがON COMMITメソッドを使用してメンテナンスされる場合は、コミット完了までの所要時間が通常より少し長くなることがあります。これは、リフレッシュ操作がコミット・プロセスの一部として実行されるためです。したがって、この方法は、多数のユーザーがマテリアライズド・ビューの基礎となる表を同時に変更する場合には適していません。

マテリアライズド・ビューで参照される表に対する挿入、更新または削除操作が、そのマテリアライズド・ビューのリフレッシュと同時に実行されると予想され、そのマテリアライズド・ビューに結合と集計が含まれている場合は、ON DEMAND高速リフレッシュではなくON COMMIT高速リフレッシュを使用することをお薦めします。

マテリアライズド・ビューがリフレッシュされなかったと考えられる場合は、アラート・ログまたはトレース・ファイルをチェックしてください。

COMMITによるリフレッシュ時にマテリアライズド・ビューがリフレッシュされなかった場合は、トレース・ファイルに示されたエラーを解決してから、DBMS_MVIEWパッケージを使用してリフレッシュ・プロシージャを明示的に起動する必要があります。この操作を行わないかぎり、マテリアライズド・ビューはコミット時に自動的にリフレッシュされません。

COMPLETE、FAST、FORCEおよびNEVERの4つのオプションのいずれかを選択すると、ディテール表からのマテリアライズド・ビューのリフレッシュ方法を指定できます。表9-5にリフレッシュ・オプションを示します。

表9-5 リフレッシュ・オプション

リフレッシュ・オプション

説明

COMPLETE

マテリアライズド・ビューの定義問合せを再計算することでリフレッシュします。

FAST

マテリアライズド・ビュー・ログに記録された情報を使用するか、SQL*Loaderダイレクト・パスまたはパーティション・メンテナンス操作によって、マテリアライズド・ビューに増分変更を適用してリフレッシュします。

FORCE

可能な場合は、FASTリフレッシュが適用されます。それ以外の場合は、COMPLETEリフレッシュが適用されます。

NEVER

マテリアライズド・ビューはリフレッシュ機能によりリフレッシュされないことを示します。

高速リフレッシュ・オプションが使用可能かどうかは、マテリアライズド・ビューのタイプによって異なります。プロシージャDBMS_MVIEW.EXPLAIN_MVIEWをコールすると、高速リフレッシュが可能かどうかを判断できます。

また、リフレッシュ時にトラステッド制約およびQUERY_REWRITE_INTEGRITY = TRUSTEDが使用可能かどうかも特定できます。妥当性チェックが行われていないRELY制約は、すべてトラステッド制約です。たとえば、ディメンションまたはマテリアライズド・ビューに定義された、妥当性チェックが行われていない外部キー/主キーのリレーションシップまたは機能依存性は、UNKNOWN状態です。リフレッシュ時のクエリー・リライトが使用可能な場合は、よりパフォーマンスの高いクエリー・リライトを使用可能にすると、これらによってリフレッシュのパフォーマンスを向上できます。リフレッシュにTRUSTED制約を使用できるマテリアライズド・ビューは、リフレッシュの後も、最新であると信頼できる状態(UNKNOWN状態)のままになります。

この状態は、ビューUSER_MVIEWSの列STALENESSに反映されます。同じビューの列UNKNOWN_TRUSTED_FDも、Y(Yes)に設定されます。

マテリアライズド・ビューのこのプロパティは、作成時にREFRESH USING TRUSTED [ENFORCED] CONSTRAINTSを指定するか、またはALTER MATERIALIZED VIEW DDLを使用して定義できます。

表9-6 制約

使用する制約

説明

TRUSTED CONSTRAINTS

リフレッシュの際、トラステッド制約およびQUERY_REWRITE_INTEGRITY = TRUSTEDを使用できます。これにより、UNKNOWN状態またはFRESH状態のマテリアライズド・ビューに対し、リフレッシュ時に妥当性チェックが行われていないRELY制約を使用してリライトを実行できます。

USING TRUSTED CONSTRAINTS句では、NULL以外の仮想プライベート・データベース(VPD)ポリシーを持つ表の最上位にマテリアライズド・ビューを作成できます。この場合、マテリアライズド・ビューが適切に動作すること確認します。マテリアライズド・ビューの結果はVPDポリシーでフィルタされた行および列に基づき計算されます。したがって、マテリアライズド・ビュー定義とVPDポリシーを調整して、必ず正しい結果が得られるようにします。USING TRUSTED CONSTRAINTS句を使用しない場合、実表のVPDポリシーにより、マテリアライズド・ビューは作成されません。

ENFORCED CONSTRAINTS

リフレッシュの際、妥当性チェック済の制約およびQUERY_REWRITE_INTEGRITY = ENFORCEDを使用できます。これにより、FRESH状態のマテリアライズド・ビューに対し、リフレッシュ時に妥当性チェック済の規定された制約のみを使用してリライトを実行できます。

マテリアライズド・ビューの高速リフレッシュは、結合列で使用可能な主キー制約および外部キー制約を使用して最適化されます。この外部キー/主キーによる最適化によって、リフレッシュのパフォーマンスは格段に向上します。たとえば、ファクト表とディメンション表の結合を含むマテリアライズド・ビューがあり、最後のリフレッシュ以降、ディメンション表のみに新しい行が挿入されファクト表は変更されていない場合、このマテリアライズド・ビューに関しては何もリフレッシュされません。これは、ディメンション表の結合列の主キー制約およびファクト表の結合列の外部キー制約により、ディメンション表に挿入された新しい行がファクト表のいずれの行とも結合せず、リフレッシュ対象がないためです。リフレッシュの最適化に関する別の例として、ファクト表とディメンション表の両方に対して、最後のリフレッシュ以降に挿入が実行されたとします。この場合、Oracle Databaseによってデルタ・ファクト表とディメンション表の結合のみが実行されます。外部キー/主キーによる最適化を行わない場合、リフレッシュ時に2つの結合が必要になります。デルタ・ファクトとディメンション表の結合、およびデルタ・ディメンションと挿入前のファクト表のイメージの結合です。

このように結合列で主キー制約および外部キー制約を使用して最適化された高速リフレッシュは、制約を強制して、または強制せずに使用できます。前者の場合は、主キー制約および外部キー制約がOracle Databaseによって強制されます。ただし、制約の管理コストがかかります。後者の場合は、アプリケーションによって主キーと外部キーの関係が保証されるため、制約はRELY NOVALIDATEを使用して宣言され、マテリアライズド・ビューはREFRESH FAST USING TRUSTED CONSTRAINTSオプションを使用して定義されます。

関連項目:
リフレッシュの制限の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

高速リフレッシュにおける一般的な制限

マテリアライズド・ビューの定義問合せは、次のように制限されています。

- マテリアライズド・ビューには、SYSDATEやROWNUMなど、結果の再現が不可能な式への参照を含めることはできません。

- マテリアライズド・ビューには、RAWまたはLONG RAWデータ型への参照を含めることはできません。

- SELECT構文のリストに副問合せを含めることはできません。

- SELECT句には分析関数(RANKなど)を指定できません。

- MODEL句を含めることはできません。

- 副問合せでHAVING句を指定できません。

- ANY、ALLまたはNOT EXISTSを含むネストした問合せは使用できません。

- [START WITH …] CONNECT BY句を含めることはできません。

- 別々のサイトに複数のディテール表を含めることはできません。

- ON COMMITのマテリアライズド・ビューにリモート・ディテール表を含めることはできません。

- ネステッド・マテリアライズド・ビューには結合または集計が必須です。

- GROUP BY句を含むマテリアライズド結合ビューおよびマテリアライズド集計ビューは索引構成表から選択できません。

結合のみを含むマテリアライズド・ビューの高速リフレッシュに関する制限

結合のみを含み、集計を含まないマテリアライズド・ビューの定義問合せには、高速リフレッシュに関して次の制限があります。

- 「高速リフレッシュにおける一般的な制限」のすべての制限が適用されます。

- GROUP BY句または集計を含めることはできません。

- FROMリスト内のすべての表のROWIDが、問合せのSELECT構文のリストにある必要があります。

- マテリアライズド・ビュー・ログが、問合せのFROMリストにあるすべての実表のROWIDを含む必要があります。

- オブジェクト型の列を含む単純結合がSELECT文に指定されている場合、複数の表から高速リフレッシュ可能なマテリアライズド・ビューを作成できません。

また、次に該当する場合は、選択したリフレッシュ方法が効率的に実行されません。

- 定義問合せに、内部結合のように動作する外部結合が使用されている。このような結合が定義問合せに含まれている場合は、内部結合を使用するように定義問合せをリライトすることを検討してください。

- マテリアライズド・ビューのSELECT構文のリストに複数の表の列の式が含まれている。

集計を含むマテリアライズド・ビューの高速リフレッシュに関する制限

集計または結合を含むマテリアライズド・ビューの定義問合せには、高速リフレッシュに関して次の制限があります。

- 「高速リフレッシュにおける一般的な制限」のすべての制限が適用されます。

高速リフレッシュは、ON COMMITおよびON DEMANDマテリアライズド・ビューの両方についてサポートされますが、次の制限が適用されます。

- マテリアライズド・ビューのすべての表にはマテリアライズド・ビュー・ログが必要であり、マテリアライズド・ビュー・ログには次のことが必要です。

- マテリアライズド・ビューで参照される表のすべての列を含んでいること。ただし、これらの列のうち、実表の列は暗号化できない。

- ROWIDおよびINCLUDING NEW VALUESで指定すること。

- 表に挿入/ダイレクト・ロード、削除および更新が混在する場合は、SEQUENCE句を指定すること。

- 高速リフレッシュについてサポートされる集計関数は、SUM、COUNT、AVG、STDDEV、VARIANCE、MINおよびMAXのみです。

- COUNT(*)を指定する必要があります。

- 集計関数は、式の最も外側でのみ使用する必要があります。つまり、AVG(AVG(x))やAVG(x)+ AVG(x)などの集計は実行できません。

- AVG(expr)などの集計ごとに、対応するCOUNT(expr)が存在している必要があります。さらにSUM(expr)を指定することをお薦めします。詳細は、表9-2を参照してください。

- VARIANCE(expr)またはSTDDEV(expr)が指定された場合は、COUNT(expr)およびSUM(expr)を指定する必要があります。さらにSUM(expr *expr)を指定することをお薦めします。詳細は、表9-2を参照してください。

- 定義問合せのSELECT列は、複数の実表の列を含む複合式にはできません。これに対する解決策の1つとして、ネステッド・マテリアライズド・ビューを使用します。

- SELECT構文のリストには、すべてのGROUP BY列が含まれる必要があります。

- マテリアライズド・ビュー・ログのフィルタ列でCHARデータ型を使用する場合、マスター・サイトおよびマテリアライズド・ビューのキャラクタ・セットは同じである必要があります。

- マテリアライズド・ビューに次のいずれかが含まれる場合は、従来のDMLの挿入およびダイレクト・ロードに対してのみ高速リフレッシュがサポートされます。

- MINまたはMAX集計を含むマテリアライズド・ビュー

- SUM(expr)を含むがCOUNT(expr)を含まないマテリアライズド・ビュー

- COUNT(*)を含まないマテリアライズド・ビュー

このようなマテリアライズド・ビューは、挿入専用マテリアライズド・ビューと呼ばれます。

- MAXまたはMINを含むマテリアライズド・ビューは、WHERE句がなければ、削除文または混在型のDML文の後に高速リフレッシュできます。

- 定義問合せのFROM句にビューまたは副問合せを含むマテリアライズド・ビューは、そのビューを完全にマージできれば、高速リフレッシュできます。マージするビューについては、『Oracle Databaseパフォーマンス・チューニング・ガイド』を参照してください。

- 外部結合がない場合は、WHERE句に任意の絞込み選択および結合を使用できます。

- 外部結合を含み、集計を含むマテリアライズド・ビューは、外部表のみが変更される場合は、従来のDMLおよびダイレクト・ロードの後に高速リフレッシュできます。また、内部結合表の結合列に一意制約が必要です。外部結合がある場合、すべての結合は、ANDで接続し、等価(=)演算子を使用する必要があります。

- CUBE、ROLLUP、グルーピング・セットまたはその連結を含むマテリアライズド・ビューの場合は、次の制限が適用されます。

- SELECT構文のリストには、グルーピング識別子を含める必要があります。すなわち、GROUP BYのすべての式にGROUPING_ID関数を含めるか、GROUP BYの各式に対して1つずつGROUPING関数を含める必要があります。たとえば、マテリアライズド・ビューのGROUP BY句が「GROUP BY CUBE(a, b)」の場合、マテリアライズド・ビューを高速リフレッシュ可能にするには、SELECT構文のリストに「GROUPING_ID(a, b)」または「GROUPING(a) AND GROUPING(b)」を含める必要があります。

- GROUP BYの結果、グルーピングが重複しないようにします。たとえば、「GROUP BY a, ROLLUP(a, b)」は、グルーピングの結果が「(a), (a, b), AND (a)」と重複するため、高速リフレッシュできません。

UNION ALLを含むマテリアライズド・ビューの高速リフレッシュに関する制限

UNION ALL集合演算子を含むマテリアライズド・ビューは、次の条件が満たされる場合に、REFRESH FASTオプションをサポートします。

- 定義問合せの最上位レベルにUNION ALL演算子を含める必要があります。
UNION ALL演算子は、1つの例外を除き、副問合せ内に埋め込むことはできません。この例外とは、定義問合せの形式がSELECT * FROM(UNION ALLを含むビューまたは副問合せ)の場合に、UNION ALLを副問合せのFROM句に指定できるということです。この例を次に示します。

CREATE VIEW view_with_unionall AS
(SELECT c.rowid crid, c.cust_id, 2 umarker
FROM customers c WHERE c.cust_last_name = 'Smith'
UNION ALL
SELECT c.rowid crid, c.cust_id, 3 umarker
FROM customers c WHERE c.cust_last_name = 'Jones');

CREATE MATERIALIZED VIEW unionall_inside_view_mv
REFRESH FAST ON DEMAND AS
SELECT * FROM view_with_unionall;

ビューview_with_unionallは、高速リフレッシュの要件を満たしていることに注意してください。

- UNION ALL問合せ内の各問合せブロックは、集計を含む高速リフレッシュ可能マテリアライズド・ビューまたは結合を含む高速リフレッシュ可能マテリアライズド・ビューの要件を満たす必要があります。
対応するタイプの高速リフレッシュ可能マテリアライズド・ビューで必要となる、適切なマテリアライズド・ビュー・ログを表に作成する必要があります。

Oracle Databaseでは、SELECT構文のリストとマテリアライズド・ビュー・ログにROWID列が含まれている場合にのみ、結合を含む単一表マテリアライズド・ビューも特殊なケースとして使用できます。これは、ビューview_with_unionallの定義問合せに示されています。

- 各問合せのSELECT構文のリストにはUNION ALLマーカーを含める必要があり、UNION ALL列の各UNION ALLブランチには個別の定数値または文字列値を含める必要があります。さらに、マーカー列は、各問合せブロックのSELECT構文のリスト内で同じ順序の場所に指定する必要があります。UNION ALLマーカーの詳細は、「UNION ALLマーカー」を参照してください。

- 外部結合、挿入専用集計マテリアライズド・ビューの問合せ、リモート表などの機能は、UNION ALLを含むマテリアライズド・ビューではサポートされません。ただし、レプリケーションで使用されるマテリアライズド・ビューで、結合または集計を含まないものは、UNION ALLまたはリモート表が使用されるときに高速リフレッシュが可能です。

- UNION ALLを含む高速リフレッシュ可能マテリアライズド・ビューを作成するには、COMPATIBILITY初期化パラメータを9.2.0以上に設定する必要があります。

リフレッシュの目的の達成

この章を通して説明しているEXPLAIN_MVIEWプロシージャの他に、DBMS_ADVISOR.TUNE_MVIEWプロシージャを使用してCREATE MATERIALIZED VIEW文を最適化し、REFRESH FASTおよびENABLE QUERY REWRITEという目的を達成できます。

ネステッド・マテリアライズド・ビューのリフレッシュ

ネステッド・マテリアライズド・ビューは、そのデータが、基になるディテール表のデータと同期されていれば、最新であるとみなされます。これは、一部のディテール表が失効したマテリアライズド・ビューになる可能性がある場合でも同様です。

ネステッド・マテリアライズド・ビューは、2つの方法でリフレッシュできます。nestedフラグをTRUEに設定したDBMS_MVIEW.REFRESHと、nestedフラグをTRUEに設定した、実表に対するREFRESH_DEPENDENTです。DBMS_MVIEW.REFRESHを使用する場合は、マテリアライズド・ビュー・チェーン全体が、指定したマテリアライズド・ビューからトップダウン方式でリフレッシュされます。つまり、指定したマテリアライズド・ビューと、依存階層にあるそのすべての子マテリアライズド・ビューが順番にリフレッシュされます。DBMS_MVIEW.REFRESH_DEPENDENTを使用する場合は、チェーン全体がボトムアップでリフレッシュされます。つまり、依存階層にあるすべての親マテリアライズド・ビューは、指定した表から順番にリフレッシュされます。

例9-7 ネステッド・マテリアライズド・ビューのリフレッシュの例

次に、ネステッド・マテリアライズド・ビューをリフレッシュする文の例を示します。

DBMS_MVIEW.REFRESH('SALES_MV,COST_MV', nested => TRUE);

この文ではまず、依存性分析に基づいてsales_mvおよびcost_mvのすべての子マテリアライズド・ビューがリフレッシュされ、次に、指定された2つのマテリアライズド・ビューがリフレッシュされます。

*_MVIEWSビューのSTALE_SINCE列を問い合せると、マテリアライズド・ビューがいつ失効になったのかを確認できます。

ORDER BY句

ORDER BY句は、CREATE MATERIALIZED VIEW文で使用できます。これは、マテリアライズド・ビューを最初に作成するときのみ使用されます。完全リフレッシュまたは高速リフレッシュ時には使用されません。

大規模なマテリアライズド・ビューに対する問合せのパフォーマンスを向上させるには、ORDER BY句に指定されている順序で、マテリアライズド・ビューに行を格納します。このように最初に順序付けると、データを物理クラスタ化できます。マテリアライズド・ビューが順序付けられた列上に索引を作成する場合、その索引を使用してマテリアライズド・ビューの行にアクセスすると、物理クラスタ化によるディスクI/Oに対する時間が削減されます。

ORDER BY句は、マテリアライズド・ビューの定義の一部とはみなされません。そのため、Oracle Databaseが様々なタイプのマテリアライズド・ビュー(集計を含まないマテリアライズド結合ビューなど)を検出する方法に違いはありません。同じ理由で、クエリー・リライトは、ORDER BY句の影響を受けません。これは、CREATE TABLE ... ORDER BYの特性と似ています。

マテリアライズド・ビュー・ログ

高速リフレッシュを使用するにはマテリアライズド・ビュー・ログが必要ですが、PCTリフレッシュの場合は例外です。つまり、ディテール表でマテリアライズド・ビューのPCTがサポートされている場合、そのマテリアライズド・ビューを高速リフレッシュするために、そのディテール表にマテリアライズド・ビュー・ログは必要ありません。しかし原則的に、高速リフレッシュを使用する場合は、マテリアライズド・ビュー・ログを作成する必要があります。マテリアライズド・ビュー・ログは、CREATE MATERIALIZED VIEW LOG文を使用して、変更が行われる実表に定義します。対象のマテリアライズド・ビューの上位に別のマテリアライズド・ビューがない場合は、前者のマテリアライズド・ビューにマテリアライズド・ビュー・ログは作成されません。これはネステッド・マテリアライズド・ビューの場合に該当します。マテリアライズド・ビューを高速リフレッシュするには、通常、マテリアライズド・ビュー・ログの定義でROWID句を指定する必要があります。また、集計を含むマテリアライズド・ビューの場合は、マテリアライズド・ビューで参照される表のすべての列、INCLUDING NEW VALUES句およびSEQUENCE句を含む必要があります。集計または結合を含むローカル・マテリアライズド・ビューは、通常、WITH COMMIT SCN句を使用することにより高速リフレッシュのパフォーマンスを向上させることができます。

次のマテリアライズド・ビュー・ログの例は、sales表に対して作成されています。

CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID
(prod_id, cust_id, time_id, channel_id, promo_id, quantity_sold, amount_sold)
INCLUDING NEW VALUES;

または、次のようにしてコミットSCNベースのマテリアライズド・ビュー・ログを作成することも可能です。

CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID
(prod_id, cust_id, time_id, channel_id, promo_id, quantity_sold, amount_sold),
COMMIT SCN INCLUDING NEW VALUES;

混在型のDML操作(複数の表に対するINSERT、UPDATEまたはDELETE操作の組合せ)を実行しないことが確実でないかぎり、キーワードSEQUENCEをマテリアライズド・ビュー・ログ文に含めることをお薦めします。複数の表に対するINSERT文、UPDATE文またはDELETE文の組合せでの高速リフレッシュをサポートするには、マテリアライズド・ビュー・ログにSEQUENCE列が必要です。ただし、マテリアライズド・ビュー・ログの作成後にSEQUENCE列を追加することも可能です。

混在型DML操作の境界は、マテリアライズド・ビューがON COMMITであるかON DEMANDであるかによって決定されます。

- ON COMMITの場合、マテリアライズド・ビューのリフレッシュはトランザクションのコミット時に発生するため、混在型DML文は同じトランザクション内で発生します。

- ON DEMANDの場合、混在型DML文はリフレッシュとリフレッシュの間に発生します。次のマテリアライズド・ビュー・ログの例は、SEQUENCEキーワードを含むsales表に対して作成されています。

CREATE MATERIALIZED VIEW LOG ON sales WITH SEQUENCE, ROWID
(prod_id, cust_id, time_id, channel_id, promo_id,
quantity_sold, amount_sold) INCLUDING NEW VALUES;

マテリアライズド・ビュー・ログでのFORCEオプションの使用

FORCEを指定し、ADD句で指定した項目がすでにそのマテリアライズド・ビュー・ログに指定されていた場合、Oracleはエラーを戻しません。Oracleは、既存の要素をそのまま無視し、ログに存在しない項目をマテリアライズド・ビュー・ログに追加します。たとえば、cust_idなどのフィルタ列を追加したが、この列がすでに存在していた場合、Oracle Databaseはこの重複を無視し、エラーも戻しません。

マテリアライズド・ビュー・ログのパージ

マテリアライズド・ビュー・ログは、マテリアライズド・ビューのリフレッシュ・プロセス中にパージ(消去)できますが、後でパージすることも可能です。後でパージすれば、リフレッシュのパフォーマンスに影響を与えません。パージを実行するタイミングは、次のようにPURGE句を使用して指定できます。

CREATE MATERIALIZED VIEW LOG ON sales
PURGE START WITH sysdate NEXT sysdate+1
WITH ROWID
(prod_id, cust_id, time_id, channel_id, promo_id, quantity_sold, amount_sold)
INCLUDING NEW VALUES;

次のような文を発行すると、パージ情報についてUSER_MVIEW_LOGSを問い合せることもできます。

SELECT PURGE_DEFERRED, PURGE_INTERVAL, LAST_PURGE_DATE, LAST_PURGE_STATUS
FROM USER_MVIEW_LOGS
WHERE LOG_OWNER "SH" AND MASTER = 'SALES';

マテリアライズド・ビュー・ログの作成時にパージを設定できるだけでなく、次のような文を発行することにより既存のマテリアライズド・ビュー・ログを変更することもできます。

ALTER MATERIALIZED VIEW LOG ON sales PURGE IMMEDIATE;

関連項目:
マテリアライズド・ビュー・ログの構文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

Oracle Enterprise Managerの使用

マテリアライズド・ビューは、Enterprise Managerのマテリアライズド・ビュー・オブジェクトを選択することで、作成することもできます。この方法が使用された場合でも、必要な情報は同じです。

マテリアライズド・ビューとNLSパラメータの使用

ある種のマテリアライズド・ビューを使用する場合は、NLSパラメータが作成時と同じに設定されているかどうかを確認する必要があります。この制限を伴うマテリアライズド・ビューは、次のとおりです。

- NLSパラメータの設定に応じて異なる値を戻す式。たとえば、(date > "01/02/03")や(rate <= "2.150")は、NLSパラメータに依存する式です。

- 結合の一方の側が文字データである等価結合。この等価結合の結果は照合に依存します。また、クエリー・リライトの場合に不適切な結果となったり、リフレッシュ操作後にマテリアライズド・ビューの一貫性がなくなるなど、セッションごとに状態が変化することがあります。

- マテリアライズド・ビューのSELECT構文のリスト内、または集計を含むマテリアライズド・ビューの集計内で、文字データへの内部変換を生成する式。この制限は、aとbが数値フィールドである場合のa+bのように、数値データのみを伴う式には適用されません。

マテリアライズド・ビューへのコメントの追加

マテリアライズド・ビューにはコメントを追加できます。たとえば、次の文は、既存のマテリアライズド・ビューに関するコメントをデータ・ディクショナリ・ビューに追加します。

COMMENT ON MATERIALIZED VIEW sales_mv IS 'sales materialized view';

この文の実行後にコメントを表示するには、カタログ・ビューの{USER, DBA} ALL_MVIEW_COMMENTSを問い合せます。たとえば、次の例を考えてみます。

SELECT MVIEW_NAME, COMMENTS
FROM USER_MVIEW_COMMENTS WHERE MVIEW_NAME = 'SALES_MV';

出力は次のようになります。

MVIEW_NAME                      COMMENTS
-----------      -----------------------
SALES_MV         sales materialized view

注意: COMPATIBILITYが10.0.1以上に設定されている場合、マテリアライズド・ビューのコンテナ表に対してCOMMENT ON TABLEは発行できません。この文を発行すると、次のエラー・メッセージが戻されます。

ORA-12098: cannot comment on the materialized view.

事前作成表にコメントが設定されている場合にマテリアライズド・ビューを作成すると、そのコメントがマテリアライズド・ビューに継承されます。既存のコメントには、'(from table)'という接頭辞が付けられます。たとえば、売上のサマリー情報を格納するための表sales_summaryが作成されているとします。この表には、'Sales summary data'というコメントがすでに関連付けられています。ここで、この事前作成表をコンテナ表として使用する同じ名前のマテリアライズド・ビューを作成するとします。マテリアライズド・ビューの作成後、コメントは'(from table) Sales summary data'になります。

ただし、事前作成表sales_summaryにコメントが設定されていない場合は、'Sales summary data'というコメントが追加されます。その後、このマテリアライズド・ビューを削除すると、そのコメントが'(from materialized view) Sales summary data'となって事前作成表に渡されます。

既存のマテリアライズド・ビューの登録

一部のデータ・ウェアハウスでは、通常のユーザー表にマテリアライズド・ビューが実装されています。このソリューションによって、マテリアライズド・ビューのパフォーマンスが向上しますが、次のような問題があります。

- すべてのSQLアプリケーションで、クエリー・リライトができるわけではありません。

- あるアプリケーションで定義されたマテリアライズド・ビューには、別のアプリケーションから透過的にアクセスできません。

- 一般に、高速パラレルまたは高速マテリアライズド・ビュー・リフレッシュはサポートされていません。

これらの制限があり、既存のマテリアライズド・ビューが非常に大きく、再作成にコストがかかりすぎる場合があるため、できるだけ、既存のマテリアライズド・ビューの表を登録する必要があります。ユーザー定義のマテリアライズド・ビューは、CREATE MATERIALIZED VIEW ... ON PREBUILT TABLE文で登録できます。一度登録されたマテリアライズド・ビューでは、クエリー・リライトが使用できるか、いずれかのリフレッシュ方法でメンテナンスできるか、あるいはその両方が可能です。

表の内容は、定義問合せをマテリアライズド・ビューとして登録したときに、定義問合せのマテリアライズ化を反映している必要があります。また、定義問合せの各列は、一致するデータ型を持つ表の列に対応している必要があります。ただし、WITH REDUCED PRECISIONを指定して、定義問合せの列の精度が表の列の精度とは異なるようにすることは可能です。

表およびマテリアライズド・ビューの名前は同じである必要がありますが、表は、表としての個別性を維持し、マテリアライズド・ビューの定義問合せで参照されない列を含むことができます。このような列は、非管理列と呼ばれます。リフレッシュ操作中に行が挿入されると、その行の各非管理列はそのデフォルト値に設定されます。したがって、非管理列は、デフォルト値を持たないかぎり、NOT NULL制約を持つことはできません。

事前作成表に基づくマテリアライズド・ビューは、パラメータQUERY_REWRITE_INTEGRITYがSTALE_TOLERATEDまたはTRUSTEDに設定されている場合に、クエリー・リライトによる選択の対象になります。整合性レベルの詳細は、第18章「基本的なクエリー・リライト」を参照してください。

事前作成表に作成されたマテリアライズド・ビューを削除しても、その表は残り、マテリアライズド・ビューのみが削除されます。

次の例では、ユーザー定義表の登録に必要な2つのステップを示しています。まず、表が作成され、次にマテリアライズド・ビューが表と同じ名前で定義されます。このマテリアライズド・ビューsum_sales_tab_mvは、クエリー・リライトで使用できます。

CREATE TABLE sum_sales_tab
PCTFREE 0  TABLESPACE demo
STORAGE (INITIAL 8M) AS
SELECT s.prod_id, SUM(amount_sold) AS dollar_sales,
SUM(quantity_sold) AS unit_sales
FROM sales s GROUP BY s.prod_id;

CREATE MATERIALIZED VIEW sum_sales_tab_mv
ON PREBUILT TABLE WITHOUT REDUCED PRECISION
ENABLE QUERY REWRITE AS
SELECT s.prod_id, SUM(amount_sold) AS dollar_sales,
SUM(quantity_sold) AS unit_sales
FROM sales s GROUP BY s.prod_id;

この表は領域を節約するために圧縮することもできます。表の圧縮の詳細は、「記憶域および表の圧縮」を参照してください。

ユーザー定義のマテリアライズド・ビューは、データの更新サイクルより長いスケジュールでリフレッシュされる場合があります。たとえば、月単位のマテリアライズド・ビューは各月の月末にしか更新されないことがあります。また、マテリアライズド・ビューの値は、常に、リフレッシュが完了した期間のみを参照します。これらのマテリアライズド・ビューに対して直接作成されたレポートでは、現行の(リフレッシュが未完了の)期間にはないデータのみが暗黙的に選択されます。ユーザー定義のマテリアライズド・ビューに時間ディメンションがすでに含まれる場合、次のことに従う必要があります。

- ユーザー定義のマテリアライズド・ビューは、登録してから、更新サイクルごとに高速リフレッシュする必要があります。

- リフレッシュが完了した期間を選択するビューを作成できます。

- レポートが、ユーザー定義のマテリアライズド・ビューを直接参照するのではなく、ビューを参照するように変更する必要があります。

ユーザー定義のマテリアライズド・ビューに時間ディメンションが含まれない場合は、次のことに従う必要があります。

- 時間ディメンションを含む新しいマテリアライズド・ビューを作成します(可能な場合)。

- 新しいマテリアライズド・ビューの時間列を、ビューに集計する必要があります。

マテリアライズド・ビューに対する索引付けの選択

マテリアライズド・ビューに対して行う最も一般的な操作は、問合せの実行および高速リフレッシュですが、各操作には、異なるパフォーマンス要件があります。問合せの実行では、マテリアライズド・ビュー・キー列のすべてのサブセットがアクセスされる必要があり、これらの列のサブセット上で結合および集計が行われる必要がある場合があります。そのため、問合せの実行では、通常、各マテリアライズド・ビュー・キー列上に単一列のビットマップ索引が定義されている場合に、パフォーマンスが最適化されます。

結合のみを含むマテリアライズド・ビューに高速リフレッシュ・オプションを使用する場合は、ROWIDを含む列上に索引を作成して、リフレッシュ操作のパフォーマンスを向上させることをお薦めします。

集計を使用するマテリアライズド・ビューが高速リフレッシュ可能な場合は、CREATE MATERIALIZED VIEW文にUSING NO INDEXが指定されていないかぎり、高速リフレッシュ・プロシージャに適切な索引が作成されます。

マテリアライズド・ビューがパーティション化されている場合、マテリアライズド・ビューに対するパーティション・メンテナンス操作の実行後は索引が使用できなくなるため、高速リフレッシュを行うには索引を再作成する必要があります。

SQLアクセス・アドバイザを使用してマテリアライズド・ビューに最適な索引を判断する方法については、『Oracle Databaseパフォーマンス・チューニング・ガイド』を参照してください。

マテリアライズド・ビューの削除

マテリアライズド・ビューを削除するには、DROP MATERIALIZED VIEW文を使用します。たとえば、次の文を考えてみます。

DROP MATERIALIZED VIEW sales_sum_mv;

この文では、マテリアライズド・ビューsales_sum_mvが削除されます。ある表に対して作成されていたマテリアライズド・ビューが削除されても、元の表は削除されませんが、リフレッシュ・メカニズム機能を使用したメンテナンスやクエリー・リライトによる使用はできなくなります。また、Oracle Enterprise Managerを使用して、マテリアライズド・ビューを削除できます。

マテリアライズド・ビュー機能の分析

DBMS_MVIEW.EXPLAIN_MVIEWプロシージャを使用すると、マテリアライズド・ビューまたは作成前のマテリアライズド・ビューで可能なことを調べることができます。特に、このプロシージャにより次のことを判断できます。

- マテリアライズド・ビューが高速リフレッシュ可能かどうか

- このマテリアライズド・ビューで実行できるクエリー・リライトのタイプ

- PCTリフレッシュが可能かどうか

このプロシージャの使用方法は簡単です。DBMS_MVIEW.EXPLAIN_MVIEWをコールし、既存のマテリアライズド・ビューのスキーマ名とマテリアライズド・ビュー名を単一パラメータとして渡すのみで済みます。あるいは、作成前のマテリアライズド・ビューについてはSELECT文字列を指定したり、完全なCREATE MATERIALIZED VIEW文を指定したりできます。マテリアライズド・ビューまたは作成前のマテリアライズド・ビューが分析され、結果がデフォルトの表MV_CAPABILITIES_TABLEまたは配列MSG_ARRAYに書き込まれます。

結果をMSG_ARRAYに入れるとき以外は、EXPLAIN_MVIEWをコールする前にutlxmv.sqlスクリプトを実行する必要があるので注意してください。このスクリプトはadminディレクトリにあります。このスクリプトは、現在のスキーマにMV_CAPABILITIES_TABLEを作成します。各種機能の説明は表9-7、可能なすべてのメッセージは表9-8を参照してください。

DBMS_MVIEW.EXPLAIN_MVIEWプロシージャの使用

EXPLAIN_MVIEWプロシージャのパラメータは、次のとおりです。

- stmt_id
オプション・パラメータ。出力行を特定のEXPLAIN_MVIEWコールと関連付けるためにクライアントが提供する一意識別子です。

- mv
分析対象となる既存のマテリアライズド・ビューの名前、あるいは作成前のマテリアライズド・ビューの問合せ定義またはCREATE MATERIALIZED VIEW文全体。

- msg-array
出力を受け取るPL/SQLのVARRAY。

EXPLAIN_MVIEWでは、指定したマテリアライズド・ビューのリフレッシュ機能とリライト機能が分析され、その結果が(複数行形式で)MV_CAPABILITIES_TABLEまたはMSG_ARRAYに挿入されます。

関連項目:
DBMS_MVIEWパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

DBMS_MVIEW.EXPLAIN_MVIEW宣言

次のPL/SQL宣言はDBMS_MVIEWパッケージで行われるもので、結果を表またはVARRAYに出力する場合の、既存のマテリアライズド・ビューと作成前のマテリアライズド・ビューを説明するパラメータの順序とデータ型を示します。

MV_CAPABILITIES_TABLEへの出力を指定して、既存または作成前のマテリアライズド・ビューを説明します。

DBMS_MVIEW.EXPLAIN_MVIEW (mv           IN VARCHAR2,
stmt_id IN VARCHAR2:= NULL);

VARRAYへの出力を指定して、既存または作成前のマテリアライズド・ビューを説明します。

DBMS_MVIEW.EXPLAIN_MVIEW (mv          IN VARCHAR2,
msg_array   OUT SYS.ExplainMVArrayType);

MV_CAPABILITIES_TABLEの使用

DBMS_MVIEW.EXPLAIN_MVIEWの最も単純な使用方法の1つは、次の構造を持つ、MV_CAPABILITIES_TABLEを使用することです。

CREATE TABLE MV_CAPABILITIES_TABLE
(STATEMENT_ID      VARCHAR(30),   -- Client-supplied unique statement identifier
MVOWNER           VARCHAR(30),   -- NULL for SELECT based EXPLAIN_MVIEW
MVNAME            VARCHAR(30),   -- NULL for SELECT based EXPLAIN_MVIEW
CAPABILITY_NAME   VARCHAR(30),   -- A descriptive name of the particular
-- capability:
-- REWRITE
--   Can do at least full text match
--   rewrite
-- REWRITE_PARTIAL_TEXT_MATCH
--   Can do at leat full and partial
--   text match rewrite
-- REWRITE_GENERAL
--   Can do all forms of rewrite
-- REFRESH
--   Can do at least complete refresh
-- REFRESH_FROM_LOG_AFTER_INSERT
--   Can do fast refresh from an mv log
--   or change capture table at least
--   when update operations are
--   restricted to INSERT
-- REFRESH_FROM_LOG_AFTER_ANY
--   can do fast refresh from an mv log
--   or change capture table after any
--   combination of updates
-- PCT
--   Can do Enhanced Update Tracking on
--   the table named in the RELATED_NAME
--   column.  EUT is needed for fast
--   refresh after partitioned
--   maintenance operations on the table
--   named in the RELATED_NAME column
--   and to do non-stale tolerated
--   rewrite when the mv is partially
--   stale with respect to the table
--   named in the RELATED_NAME column.
--   EUT can also sometimes enable fast
--   refresh of updates to the table
--   named in the RELATED_NAME column
--   when fast refresh from an mv log
--   or change capture table is not
--   possible.
-- See Table 9-7
POSSIBLE          CHARACTER(1),  -- T = capability is possible
-- F = capability is not possible
RELATED_TEXT      VARCHAR(2000), -- Owner.table.column, alias name, and so on
-- related to this message. The specific
-- meaning of this column depends on the
-- NSGNO column. See the documentation for
-- DBMS_MVIEW.EXPLAIN_MVIEW() for details.
RELATED_NUM       NUMBER,        -- When there is a numeric value
-- associated with a row, it goes here.
MSGNO             INTEGER,       -- When available, QSM message # explaining
-- why disabled or more details when
-- enabled.
MSGTXT            VARCHAR(2000), -- Text associated with MSGNO.
SEQ               NUMBER);       -- Useful in ORDER BY clause when
-- selecting from this table.

MV_CAPABILITIES_TABLEを作成するには、adminディレクトリにあるutlxmv.sqlスクリプトを使用できます。

例9-8 DBMS_MVIEW.EXPLAIN_MVIEW

まず、マテリアライズド・ビューを作成します。あるいは、作成前のマテリアライズド・ビューのSELECT文またはCREATE MATERIALIZED VIEW文全体を使用して、そのマテリアライズド・ビューにEXPLAIN_MVIEWを実行することもできます。

CREATE MATERIALIZED VIEW cal_month_sales_mv
BUILD IMMEDIATE
REFRESH FORCE
ENABLE QUERY REWRITE AS
SELECT t.calendar_month_desc,  SUM(s.amount_sold) AS dollars
FROM sales s,  times t WHERE s.time_id = t.time_id
GROUP BY t.calendar_month_desc;

次に、マテリアライズド・ビューを指定してEXPLAIN_MVIEWを起動します。各行が論理順に表示されるように、ORDER BY句にSEQ列を使用する必要があります。機能が使用可能でない場合は、P列にN、MSGTXT列に説明が表示されます。複数の理由で機能が使用可能でない場合は、理由ごとに表示されます。

EXECUTE DBMS_MVIEW.EXPLAIN_MVIEW ('SH.CAL_MONTH_SALES_MV');

SELECT capability_name,  possible, SUBSTR(related_text,1,8)
AS rel_text, SUBSTR(msgtxt,1,60) AS msgtxt
FROM MV_CAPABILITIES_TABLE
ORDER BY seq;

CAPABILITY_NAME                 P    REL_TEXT     MSGTXT
---------------                 -    --------     ------
PCT                             N
REFRESH_COMPLETE                Y
REFRESH_FAST                    N
REWRITE                         Y
PCT_TABLE                       N    SALES        no partition key or PMARKER in select list
PCT_TABLE                       N    TIMES        relation is not a partitioned table
REFRESH_FAST_AFTER_INSERT       N    SH.TIMES     mv log must have new values
REFRESH_FAST_AFTER_INSERT       N    SH.TIMES     mv log must have ROWID
REFRESH_FAST_AFTER_INSERT       N    SH.TIMES     mv log does not have all necessary columns
REFRESH_FAST_AFTER_INSERT       N    SH.SALES     mv log must have new values
REFRESH_FAST_AFTER_INSERT       N    SH.SALES     mv log must have ROWID
REFRESH_FAST_AFTER_INSERT       N    SH.SALES     mv log does not have all necessary columns
REFRESH_FAST_AFTER_ONETAB_DML   N    DOLLARS      SUM(expr) without COUNT(expr)
REFRESH_FAST_AFTER_ONETAB_DML   N                 see the reason why
REFRESH_FAST_AFTER_INSERT is disabled
REFRESH_FAST_AFTER_ONETAB_DML   N                 COUNT(*) is not present in the select list
REFRESH_FAST_AFTER_ONETAB_DML   N                 SUM(expr) without COUNT(expr)
REFRESH_FAST_AFTER_ANY_DML      N                 see the reason why
REFRESH_FAST_AFTER_ONETAB_DML is disabled
REFRESH_FAST_AFTER_ANY_DML      N    SH.TIMES     mv log must have sequence
REFRESH_FAST_AFTER_ANY_DML      N    SH.SALES     mv log must have sequence
REFRESH_PCT                     N                 PCT is not possible on any of the detail
tables in the materialized view
REWRITE_FULL_TEXT_MATCH         Y
REWRITE_PARTIAL_TEXT_MATCH      Y
REWRITE_GENERAL                 Y
REWRITE_PCT                     N                 PCT is not possible on any detail tables

関連項目:

- PCTの詳細は、第16章「データ・ウェアハウスのメンテナンス」を参照してください。

- PCTの詳細は、第19章「高度なクエリー・リライト」を参照してください。

MV_CAPABILITIES_TABLE.CAPABILITY_NAMEの詳細

表9-7に、CAPABILITY_NAME列の値の説明を示します。

表9-7 CAPABILITY_NAME列の詳細

CAPABILITY_NAME

説明

PCT

この機能が使用可能な場合は、1つ以上のディテール関係のパーティション・チェンジ・トラッキング(PCT)が可能です。この機能が使用可能でない場合、マテリアライズド・ビューで参照されるディテール関係のPCTは不可能です。

REFRESH_COMPLETE

この機能が使用可能な場合は、マテリアライズド・ビューの完全リフレッシュが可能です。

REFRESH_FAST

この機能が使用可能な場合は、少なくとも特定の状況下での高速リフレッシュが可能です。

REWRITE

この機能が使用可能な場合は、少なくともテキストの完全一致のクエリー・リライトが可能です。この機能が使用可能でない場合、クエリー・リライトは形式を問わず不可能です。

PCT_TABLE

この機能が使用可能な場合は、トップ・レベルのFROMリストで指定されたパーティション表に対してPCTが適用可能です。可能な場合、PCTはRELATED_TEXT列で指定されたパーティション表に適用されます。

PCTは、RELATED_TEXT列で指定された表に対するパーティション・メンテナンス操作後の高速リフレッシュをサポートするために必要です。

また、PCTはマテリアライズド・ビュー・ログからの高速リフレッシュが不可能な場合でも、RELATED_TEXT列で指定された表の更新に関する高速リフレッシュをサポートします。

また、PCTは、RELATED_TEXT列で指定された表に関して、マテリアライズド・ビューの部分的な失効がある場合に、クエリー・リライトをサポートする目的でも必要です。

この機能が使用不可能な場合、PCTはRELATED_TEXT列で指定された表には適用されません。この場合、RELATED_TEXT列で指定された表に対するパーティション・メンテナンス操作後に、高速リフレッシュできません。また、RELATED_TEXT列で指定された表に対する更新のPCTベースのリフレッシュもできません。最終的に、RELATED_TEXT列で指定された表に関して、マテリアライズド・ビューの部分的な失効がある場合に、クエリー・リライトをサポートできません。

PCT_TABLE_REWRITE

この機能が使用可能な場合は、トップ・レベルのFROMリストで指定されたパーティション表に対してPCTが適用可能です。可能な場合、PCTはRELATED_TEXT列で指定されたパーティション表に適用されます。

この機能は、RELATED_TEXT列で指定された表に関して部分的に失効状態であるマテリアライズド・ビューへのクエリー・リライトをサポートするために必要です。

この機能が使用不可能で、このマテリアライズド・ビューが、RELATED_TEXT列で指定された表に関して部分的に失効状態である場合、クエリー・リライトはサポートされません。

REFRESH_FAST_AFTER_INSERT

この機能が使用可能であれば、少なくとも更新がINSERT操作のみに制限されている場合は、マテリアライズド・ビュー・ログからの高速リフレッシュが可能です。完全リフレッシュも可能です。この機能が使用可能でない場合、マテリアライズド・ビュー・ログからの高速リフレッシュは、形式を問わず不可能です。

RREFRESH_FAST_AFTER_ONETAB_DML

この機能が使用可能であれば、すべての更新操作が単一表に対して実行される場合は、更新操作のタイプを問わずマテリアライズド・ビュー・ログからの高速リフレッシュが可能です。この機能が使用可能でなければ、更新操作が複数の表に対して実行される場合、マテリアライズド・ビュー・ログからの高速リフレッシュが不可能な場合があります。

REFRESH_FAST_AFTER_ANY_DML

この機能が使用可能であれば、更新操作のタイプや更新される表の数を問わず、マテリアライズド・ビュー・ログからの高速リフレッシュが可能です。この機能が使用可能でなければ、更新操作(INSERT以外)が複数の表に影響する場合に、マテリアライズド・ビュー・ログからの高速リフレッシュが不可能なことがあります。

REFRESH_FAST_PCT

この機能が使用可能な場合は、PCTを使用した高速リフレッシュが可能です。通常、これは、PCTが可能として示されているディテール表に対するパーティション・メンテナンス操作後に、リフレッシュが可能であることを意味します。

REWRITE_FULL_TEXT_MATCH

この機能が使用可能な場合は、テキストの完全一致のクエリー・リライトが可能です。この機能が使用可能でない場合、テキストの完全一致のクエリー・リライトは不可能です。

REWRITE_PARTIAL_TEXT_MATCH

この機能が使用可能な場合は、少なくともテキストの完全一致および部分一致のクエリー・リライトが可能です。この機能が使用可能でない場合、少なくともテキストの部分一致と一般的なクエリー・リライトは不可能です。

REWRITE_GENERAL

この機能が使用可能な場合は、一般的なクエリー・リライトと、テキストの完全一致および部分一致のクエリー・リライトを含め、クエリー・リライト機能がすべて使用可能です。この機能が使用可能でない場合、少なくとも一般的なクエリー・リライトは不可能です。

REWRITE_PCT

この機能が使用可能な場合、クエリー・リライトでは、QUERY_REWRITE_INTEGRITY = ENFORCEDまたはTRUSTEDモードでも、部分的に失効したマテリアライズド・ビューを使用できます。この機能が使用可能でない場合、クエリー・リライトでは、QUERY_REWRITE_INTEGRITY = STALE_TOLERATEDモードの場合にのみ、部分的に失効したマテリアライズド・ビューを使用できます。

MV_CAPABILITIES_TABLE列の詳細

表9-8に、RELATED_TEXTおよびRELATED_NUM列の意味を示します。

表9-8 MV_CAPABILITIES_TABLE列の詳細

MSGNO

MSGTXT

RELATED_NUM

RELATED_TEXT

NULL

NULL

PCT機能専用。PCTが使用可能になっている表の[owner.]name

2066

Oracleエラー内に記載されます

発生したOracleエラー番号

2067

SELECTリストにパーティション・キー、PMARKERまたは結合依存式がありません

PCTがサポートされていない関係の[owner.]name

2068

リレーションがパーティション化されていません

PCTがサポートされていない関係の[owner.]name

2069

PCTでは複数列で構成されるパーティション・キーをサポートしていません

PCTがサポートされていない関係の[owner.]name

2070

PCTではこのタイプのパーティション化をサポートしていません

PCTがサポートされていない関係の[owner.]name

2071

内部エラー: PCT障害コードが定義されていません

認識されない数値PCT障害コード

PCTがサポートされていない関係の[owner.]name

2072

ネストされたマテリアライズド・ビューの高速リフレッシュのための要件は満たされていません

2077

マテリアライズド・ビュー・ログは最新の全体リフレッシュよりも新しいです

マテリアライズド・ビュー・ログが必要な表の[owner.]table_name

2078

マテリアライズド・ビュー・ログは新しい値を持つ必要があります

マテリアライズド・ビュー・ログが必要な表の[owner.]table_name

2079

マテリアライズド・ビュー・ログはROWIDを持つ必要があります

マテリアライズド・ビュー・ログが必要な表の[owner.]table_name

2080

マテリアライズド・ビュー・ログは主キーを持つ必要があります

マテリアライズド・ビュー・ログが必要な表の[owner.]table_name

2081

マテリアライズド・ビュー・ログは必要な列をすべて持っているわけではありません

マテリアライズド・ビュー・ログが必要な表の[owner.]table_name

2082

マテリアライズド・ビュー・ログの問題

マテリアライズド・ビュー・ログが必要な表の[owner.]table_name

2099

マテリアライズド・ビューはFROMリストのリモート表またはビューを参照しています

SELECTキーワードから問題の表またはビューへのオフセット

問題の表またはビューの[owner.]name

2126

複数のマスター・サイトです

最初の異なるノードの名前、または、最初の異なるノードがローカルの場合はNULL

2129

結合またはフィルタ条件が複合しています

結合またはフィルタ条件に関連する表の[owner.]name(または使用可能でない場合はNULL)

2130

式が高速リフレッシュのためにサポートされていません

SELECTキーワードから問題の式へのオフセット

問題の式のSELECTリストの別名

2150

SELECTリストはUNION演算子を超えて同一である必要があります

SELECTキーワードからSELECTリスト内の最初の異なる選択項目へのオフセット

SELECTリスト内の最初の異なる選択項目の別名

2182

PCTは結合依存性を介して使用できます

PCT_TABLE_REWRITEが使用可能になっていない関係の[owner.]name

2183

PCTを有効化する式が分析関数のPARTITION BYまたはモデルにありません

認識されない数値PCT障害コード

PCTが使用可能になっていない関係の[owner.]name

2184

PCTを有効化する式はロールアップできません

PCTが使用可能になっていない関係の[owner.]name

2185

SELECTリストにパーティション・キーまたはPMARKERがありません

PCT_TABLE_REWRITEが使用可能になっていない関係の[owner.]name

2186

GROUP OUTER JOINがあります

2187

外部表のマテリアライズド・ビュー

10 高度なマテリアライズド・ビュー

この章では、マテリアライズド・ビューの高度な使用方法について説明します。内容は次のとおりです。

- パーティション化とマテリアライズド・ビュー

- 分析処理環境でのマテリアライズド・ビュー

- マテリアライズド・ビューとモデル

- マテリアライズド・ビューの無効化

- マテリアライズド・ビューのセキュリティ問題

- マテリアライズド・ビューの変更

パーティション化とマテリアライズド・ビュー

データ・ウェアハウスに格納されているデータの量は膨大であるため、パーティション化は、データベースの設計時に非常に有効なオプションです。ファクト表のパーティション化によって、スケーラビリティの向上とシステム管理の簡素化を実現できます。また、効率的に再作成できるローカル索引を定義できるようになります。ファクト表をパーティション化することにより、マテリアライズド・ビューに対するパーティション・チェンジ・トラッキング(PCT)リフレッシュが可能になるので、マテリアライズド・ビューを高速リフレッシュできる可能性も高くなります。マテリアライズド・ビューのパーティション化は、リフレッシュの面でもメリットがあります。リフレッシュ・プロシージャは、より多くの場合にパラレルDMLを使用できるようになり、PCTベースのリフレッシュは、パーティションの切捨てを使用してマテリアライズド・ビューを効率的にメンテナンスできるようになります。パーティション化の詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

パーティション・チェンジ・トラッキング

最新の状態かどうかの追跡対象を、マテリアライズド・ビュー全体ではなく、より細かく限定でき、それによるメリットが得られます。特定のディテール表のパーティションによる影響を受けるマテリアライズド・ビュー内の行を識別する機能は、パーティション・チェンジ・トラッキングと呼ばれます。1つ以上のディテール表がパーティション化されている場合は、マテリアライズド・ビュー内で、変更されたディテール・パーティションに対応する特定の行を識別できます。これらの行はパーティションが変更されると失効しますが、他のすべての行は最新のままです。

PCTを使用すると、特定のパーティションに対応するマテリアライズド・ビューの行を識別できます。PCTは、ディテール表に対するパーティション・メンテナンス操作後の高速リフレッシュのサポートにも使用されます。たとえば、ディテール表のパーティションが切り捨てられるか削除されると、マテリアライズド・ビュー内で影響を受ける行が識別され、削除されます。

QUERY_REWRITE_INTEGRITY = ENFORCEDまたはTRUSTEDモードのときは、マテリアライズド・ビュー全体を失効とみなすのではなく、マテリアライズド・ビューの行のうち最新のものと失効しているものが識別され、最新状態の行をクエリー・リライトで使用できるようになります。DBA_MVIEW_DETAIL_PARTITIONなどの一部のビューでは、どのパーティションが失効しているかまたは最新なのかが具体的に示されます。マテリアライズド・ビューに結合依存の式があることによって、変更対象の表のパーティション・チェンジ・トラッキングが使用可能になる場合は、部分的に失効しているマテリアライズド・ビューへのリライトは行われません。詳細は、「結合依存の式」を参照してください。

PCTをサポートするには、マテリアライズド・ビューが次の要件を満たしている必要があります。

- マテリアライズド・ビューで参照される1つ以上のディテール表が、パーティション化されている必要があります。

- パーティション表には、レンジ、リストまたはコンポジットのいずれかのパーティション化を使用する必要があります。

- 最上位レベルのパーティション・キーは、単一列のみで構成する必要があります。

- マテリアライズド・ビューには、ディテール表のパーティション・キー列、パーティション・マーカー、ROWIDまたは結合依存の式のいずれかを含める必要があります。DBMS_MVIEW.PMARKERファンクションの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

- GROUP BY句を使用する場合は、パーティション・キー列、パーティション・マーカー、ROWIDまたは結合依存の式をGROUP BY句に指定する必要があります。

- 分析ウィンドウ関数またはMODEL句を使用する場合は、パーティション・キー列、パーティション・マーカー、ROWIDまたは結合依存の式を、それぞれのPARTITION BY副次句に指定する必要があります。

- データ修正の発生が可能なのは、パーティション表のみです。マテリアライズド・ビューに結合依存の式を含む表に対してPCTリフレッシュを実行する際は、いずれの結合依存の表についても、データ修正が発生していてはなりません。

- COMPATIBILITY初期化パラメータは9.0.0.0.0以上に設定する必要があります。

- ビュー、リモート表または外部結合を参照するマテリアライズド・ビューの場合、PCTはサポートされません。

パーティション・キー

パーティション・チェンジ・トラッキングは、マテリアライズド・ビューに必要な情報が存在すれば、ベースとなるディテール表のパーティションの各行を、対応するマテリアライズド・ビューの各行に関連付けることができます。そのためには、ディテール表のパーティション・キー列をSELECT構文のリストに含めます。また、GROUP BYを使用する場合は、GROUP BYリストにも含めます。

例として、日次の顧客売上を格納するマテリアライズド・ビューを考えてみます。次の例では、shサンプル・スキーマおよび3つのディテール表(sales、productsおよびtimes)を使用してマテリアライズド・ビューを作成します。sales表はtime_id列でパーティション化されており、products表はprod_id列でパーティション化されています。times表はパーティション表ではありません。

例10-1 パーティション・キー

次に例を示します。

CREATE MATERIALIZED VIEW LOG ON SALES WITH ROWID
(prod_id, time_id, quantity_sold, amount_sold) INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG ON PRODUCTS WITH ROWID
(prod_id, prod_name, prod_desc) INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG ON TIMES WITH ROWID
(time_id, calendar_month_name, calendar_year) INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW cust_dly_sales_mv
BUILD DEFERRED REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT s.time_id, p.prod_id, p.prod_name, COUNT(*),
SUM(s.quantity_sold), SUM(s.amount_sold),
COUNT(s.quantity_sold), COUNT(s.amount_sold)
FROM sales s, products p, times t
WHERE  s.time_id = t.time_id AND s.prod_id = p.prod_id
GROUP BY s.time_id, p.prod_id, p.prod_name;

cust_dly_sales_mvでは、sales表およびproducts表の両方でPCTが使用可能になります。これは、2つの表のパーティション化キー列time_idとprod_idがマテリアライズド・ビューに含まれているためです。

結合依存の式

結合依存の式とは、パーティション化キーでの等価結合を介して直接的または間接的にパーティション化ディテール表に結合されている表の列で構成され、結合キーのディメンション属性またはディメンション階層の親である式のことです。ディテール表へのパスに存在する一連の表は、結合依存の表と呼ばれます。次の問合せを考えてみます。

SELECT s.time_id, t.calendar_month_name
FROM sales s, times t WHERE s.time_id = t.time_id;

この問合せでは、times表が結合依存の表です。この表は、パーティション化キー列のtime_idでsales表に結合されているためです。また、calendar_month_nameはtimes.time_idのディメンション階層属性です。これは、calendar_month_nameがtimes.mon_idの属性で、times.mon_idがtimes.time_idのディメンション階層の親であるためです。したがって、times表からの式calendar_month_nameは結合依存の式ということになります。次に、別の例について考えてみます。

SELECT s.time_id, y.calendar_year_name
FROM sales s, times_d d, times_m m, times_y y
WHERE s.time_id = d.time_id AND d.day_id = m.day_id AND m.mon_id = y.mon_id;

ここで、times表はtimes_d、times_mおよびtimes_yの各表に非正規化されています。times_y表からの式calendar_year_nameは結合依存の式で、times_d、times_mおよびtimes_yの各表は結合依存の表です。その理由は、times_y表がtimes_m表およびtimes_d表を介して、そのパーティション化キー列time_idで間接的にsales表に結合されているためです。

これによりユーザーは、ディテール表のパーティション化キーよりも上位のレベルでの集計を含むマテリアライズド・ビューを作成できます。次に、月次の顧客売上を格納するマテリアライズド・ビューの例を見てみます。

例10-2 結合依存の式

マテリアライズド・ビュー・ログは定義済であるとすると、このマテリアライズド・ビューは、次のDDLで作成できます。

CREATE MATERIALIZED VIEW cust_mth_sales_mv
BUILD DEFERRED REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT t.calendar_month_name, p.prod_id, p.prod_name, COUNT(*),
SUM(s.quantity_sold), SUM(s.amount_sold),
COUNT(s.quantity_sold), COUNT(s.amount_sold)
FROM sales s, products p, times t
WHERE  s.time_id = t.time_id AND s.prod_id = p.prod_id
GROUP BY t.calendar_month_name, p.prod_id, p.prod_name;

ここでは、結合依存の表timesと、times.calendar_month_nameはtimes.time_idによって決定されるディメンション属性であるというリレーションシップを使用して、ディテール表の行を、対応するマテリアライズド・ビューの行に関連付けることができます。これにより、sales表のパーティション・チェンジ・トラッキングが可能になります。さらに、マテリアライズド・ビューにproducts表のパーティション化キー列prod_idが含まれているので、products表のPCTも可能になります。

パーティション・マーカー

DBMS_MVIEW.PMARKERファンクションは、マテリアライズド・ビューのカーディナリティを大幅に削減するように設計されています(例については、例10-3を参照)。このファンクションは、指定のパーティション表の指定の行についてパーティションを一意に識別するパーティション識別子を戻します。したがって、DBMS_MVIEW.PMARKERファンクションは、SELECT句およびGROUP BY句のパーティション・キー列のかわりに使用されます。

マテリアライズド・ビューでのPL/SQLファンクションの一般的なケースとは異なり、DBMS_MVIEW.PMARKERを使用すると、リライト・モードがQUERY_REWRITE_INTEGRITY = ENFORCEDであっても、そのマテリアライズド・ビューでのリライトは可能です。

PMARKERファンクションの使用例として、指定した年の製品カテゴリ別収益などの典型的な数値の計算を考えてみます。毎月1,000種類の製品が販売されていた場合、マテリアライズド・ビューには12,000行が格納されていることになります。

例10-3 パーティション・マーカー

例として、製品カテゴリごとの年間売上収益を格納するマテリアライズド・ビューを考えます。各製品カテゴリにはおよそ数百種類の製品があるので、products表のパーティション化キー列prod_idをマテリアライズド・ビューに含めると、カーディナリティはかなり高くなります。このマテリアライズド・ビューでは、かわりにDBMS_MVIEW.PMARKERファンクションを使用します。この場合、マテリアライズド・ビューのカーディナリティは、products表のパーティションの数だけ倍加します。

CREATE MATERIALIZED VIEW prod_yr_sales_mv
BUILD DEFERRED
REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT DBMS_MVIEW.PMARKER(p.rowid), p.prod_category, t.calendar_year, COUNT(*),
SUM(s.amount_sold), SUM(s.quantity_sold),
COUNT(s.amount_sold), COUNT(s.quantity_sold)
FROM   sales s, products p, times t
WHERE  s.time_id = t.time_id AND s.prod_id = p.prod_id
GROUP BY  DBMS_MVIEW.PMARKER (p.rowid), p.prod_category, t.calendar_year;

prod_yr_sales_mvのSELECT構文のリストには、products表に対するDBMS_MVIEW.PMARKERファンクションが含まれています。これにより、パーティション・キー列prod_idでグルーピングした場合よりもカーディナリティに与える影響を大幅に抑えながら、products表のパーティション・チェンジ・トラッキングが可能になります。この例では、prod_yr_sales_mvに必要な集計レベルは、products.prod_category別にグルーピングすることです。DBMS_MVIEW.PMARKERファンクションを使用した場合、マテリアライズド・ビューのカーディナリティは、products表のパーティションの数だけ倍加するだけで済みます。通常、この方法では、パーティション・キー列を含めた場合より、カーディナリティへの影響が大幅に小さくなります。

sales表でパーティション・チェンジ・トラッキングが使用可能になることに注意してください。これは、結合依存の式calendar_yearがSELECT構文のリストに含まれているためです。

部分的なリライト

後続のINSERT文で、表salesのsales_part3パーティションに新しい行が追加されると仮定します。cust_dly_sales_mvは、パーティション・キーを使用して表salesでのPCTが可能になっているため、Oracleはこの時点で、sales_part3パーティションに対応するマテリアライズド・ビューcust_dly_sales_mvの失効している行を識別できます(その他の行は最新状態なので変更されません)。クエリー・リライトは、マテリアライズド・ビューcust_mth_sales_mvおよびprod_yr_sales_mvの最新部分を識別できません。これは、PCTが結合依存の式を使用して表salesで使用可能になっているためです。クエリー・リライトは、PCTがパーティション・キーまたはパーティション・マーカーを使用してディテール表で使用可能になっている場合にのみ、ディテール表への変更発生時にマテリアライズド・ビューの最新部分を識別できます。

マテリアライズド・ビューのパーティション化

次の例に示すように、マテリアライズド・ビューのパーティション化には、Oracleの標準パーティション化句を使用したマテリアライズド・ビューの定義が含まれます。この文では、part_sales_mvというマテリアライズド・ビューが作成されます。このマテリアライズド・ビューは3つのパーティションを使用し、高速リフレッシュが可能で、クエリー・リライトに使用できます。

CREATE MATERIALIZED VIEW part_sales_mv
PARALLEL PARTITION BY RANGE (time_id)
(PARTITION month1
VALUES LESS THAN (TO_DATE('31-12-1998', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M)
TABLESPACE sf1,
PARTITION month2
VALUES LESS THAN (TO_DATE('31-12-1999', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M)
TABLESPACE sf2,
PARTITION month3
VALUES LESS THAN (TO_DATE('31-12-2000', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M)
TABLESPACE sf3)
BUILD DEFERRED
REFRESH FAST
ENABLE QUERY REWRITE AS
SELECT s.cust_id, s.time_id,
SUM(s.amount_sold) AS sum_dol_sales, SUM(s.quantity_sold) AS sum_unit_sales
FROM sales s GROUP BY s.time_id, s.cust_id;

事前作成表のパーティション化

次に示すように、マテリアライズド・ビューは、パーティション化された事前作成表に登録できます。

CREATE TABLE part_sales_tab_mv(time_id, cust_id, sum_dollar_sales, sum_unit_sale)
PARALLEL PARTITION BY RANGE (time_id)
(PARTITION month1
VALUES LESS THAN (TO_DATE('31-12-1998', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M)
TABLESPACE sf1,
PARTITION month2
VALUES LESS THAN (TO_DATE('31-12-1999', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M)
TABLESPACE sf2,
PARTITION month3
VALUES LESS THAN (TO_DATE('31-12-2000', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M)
TABLESPACE sf3)  AS
SELECT s.time_id, s.cust_id, SUM(s.amount_sold) AS sum_dollar_sales,
SUM(s.quantity_sold) AS sum_unit_sales
FROM sales s GROUP BY s.time_id, s.cust_id;

CREATE MATERIALIZED VIEW part_sales_tab_mv
ON PREBUILT TABLE
ENABLE QUERY REWRITE AS
SELECT s.time_id,  s.cust_id, SUM(s.amount_sold) AS sum_dollar_sales,
SUM(s.quantity_sold) AS sum_unit_sales
FROM sales s GROUP BY s.time_id, s.cust_id;

この例では、part_sales_tab_mv表は3か月ごとにパーティション化され、事前作成表を使用するためにマテリアライズド・ビューが登録されています。このマテリアライズド・ビューは、ENABLE QUERY REWRITE句を含んでいるため、クエリー・リライトに使用できます。

マテリアライズド・ビューのパーティション化によるメリット

マテリアライズド・ビューをディテール表のパーティション化キー列または結合依存の式でパーティション化すると、TRUNCATE PARTITION文を使用してリフレッシュの際にマテリアライズド・ビューの1つ以上のパーティションを削除し、後でそのパーティションに新しいデータを再移入するという作業がより効率的になります。Oracle Databaseは、次の条件と「パーティション・チェンジ・トラッキング」で説明したその他の条件が満たされている場合に、パーティションの切捨てを伴うこの種の高速リフレッシュ(PCTリフレッシュと呼ぶ)を使用します。

- マテリアライズド・ビューが、ディテール表のパーティション化キー列または結合依存の式でパーティション化されている。

- パーティション化キー列または結合式を使用してPCTが使用可能になっている場合、マテリアライズド・ビューはレンジ・パーティション化またはリスト・パーティション化されている必要がある。

- PCTリフレッシュがアトミックではない。

ローリング・マテリアライズド・ビュー

データ・ウェアハウスまたはデータ・マートに時間ディメンションが含まれる場合、通常は最も古い情報をアーカイブしてから、その記憶域を新しい情報に再使用する必要があります。これは、ローリング・ウィンドウ・シナリオと呼ばれます。ファクト表またはマテリアライズド・ビューに時間ディメンションが含まれ、時間属性によって水平にパーティション化されている場合、ロールアウトされるデータの量がレンジ・パーティション化の場合と等しいか、または少なくとも整列していれば、ローリング・マテリアライズド・ビューの管理が、高速で管理コストの低いパーティション管理のみに軽減されます。

データ・ウェアハウスにローリング・マテリアライズド・ビューを作成する場合は、パーティション・メンテナンス操作を実行する頻度を決定する必要があります。また、ファクト表およびマテリアライズド・ビューをパーティション化して、古いデータが必要なくなったときに必要なシステム管理によるオーバーヘッドを削減する必要があります。また、頻繁には更新されないパーティションに対してはデータ圧縮の使用も考慮します。

レンジ・パーティション化の使用には、制限はありません。たとえば、時間値およびキー値の両方を使用したコンポジット・パーティション化が、データに対して適切なパーティション・ソリューションとなる場合もあります。

CONSIDER FRESHおよび圧縮の詳細は、第16章「データ・ウェアハウスのメンテナンス」を参照してください。

分析処理環境でのマテリアライズド・ビュー

この項では、分析SQLで使用される概念と、リレーショナル・データベースでこのタイプの問合せを処理する方法を説明します。また、一般的な例を使用してマテリアライズド・ビューを作成する最適な方法を示します。

キューブ

データ・ウェアハウス環境では一般にデータをスター・スキーマ形式で表示しますが、分析SQL問合せではデータを階層的キューブ形式で表示します。階層的キューブにはそのディメンションそれぞれのロールアップ階層に沿って集計されたデータが含まれ、これらの集計はディメンションをまたがって結合されます。ビジネス・インテリジェンス問合せに必要な典型的な集計の集合が含まれます。

例10-4 階層的キューブ

次の2つのディメンションを持つ売上データセットがあり、それぞれのディメンションに4レベルの階層があるとします。

- 時間。(all times)、year、quarterおよびmonthを含みます。

- 製品。(all products)、division、brandおよびitemを含みます。

つまり、階層的キューブ内には16個の集計グループがあることになります。これは、時間の4つのレベルと製品の4つのレベルを掛け合せてキューブが生成されるためです。各ディメンションの4つのレベルを表10-1に示します。

表10-1 時間別および製品別ROLLUP

時間別ROLLUP

製品別ROLLUP

year、quarter、month

division、brand、item

year、quarter

division、brand

year

division

all times

all products

ディメンションとレベルの数を増やすと、計算するグループ数が急激に増えることに注意してください。この例には16個のグループが含まれていますが、同数レベルのディメンションを2つ追加するだけで、4×4×4×4 = 256個のグループになります。また、ディメンションに階層が複数あると、同じようにグループ数が増えることも考慮してください。たとえば、時間ディメンションにもう1つの会計月という階層があるとします。会計月は会計四半期、さらに会計年にロールアップします。爆発的に増えるグループの処理は、歴史的に、オンライン分析処理システム用のデータ格納での大きな課題です。

典型的なオンライン分析問合せでは、キューブの様々な部分をスライスおよびダイスして、1つのレベルの集計と別のレベルの集計を比較します。たとえば、問合せにより、2002年1月の食料雑貨部門の売上を求め、2001年全体の食料雑貨部門の売上合計とこの値を比較する場合があります。

マテリアライズド・ビューのパーティション化によるメリット

複数の集計グループを含むマテリアライズド・ビューのパフォーマンスがリフレッシュおよびクエリー・リライト向けに最大化されるのは、適切にパーティション化されている場合です。

ローリング・ウィンドウ・シナリオでのPCTリフレッシュには、時間ディメンションのいくつかのレベルにおける最上位レベルでのパーティション化が必要です。また、このマテリアライズド・ビューへのクエリー・リライトでパーティション・プルーニングを行うには、GROUPING_ID列でのパーティション化が必要です。したがって、これらのマテリアライズド・ビューのパーティション化方法として最も効果的なのは、コンポジット・パーティション化((time, GROUPING_ID)列でのレンジ-リスト・パーティション化)ということになります。この方法でマテリアライズド・ビューをパーティション化すると、次のことが可能になります。

- PCTリフレッシュ: リフレッシュのパフォーマンスが向上します。

- パーティション・プルーニング: 関連する集計グループのみがアクセスされるため、問合せの処理コストが大幅に削減されます。

PCTリフレッシュを使用しない場合は、GROUPING_ID列のみでリスト・パーティション化します。

マテリアライズド・ビューの圧縮

冗長性の高いデータ(多数の外部キーを持つ表など)を使用するときは、データ圧縮を考慮するようにします。特に、ROLLUP句を使用して作成したマテリアライズド・ビューがこの候補になります。データ圧縮の構文および制限については、『Oracle Database SQL言語リファレンス』を参照してください。圧縮の詳細は、「記憶域および表の圧縮」を参照してください。

集合演算子を含むマテリアライズド・ビュー

Oracle Databaseでは、定義問合せに集合演算子が含まれるマテリアライズド・ビューをサポートしています。集合演算子を含むマテリアライズド・ビューをクエリー・リライト対応として作成できるようになりました。マテリアライズド・ビューは、ON COMMITまたはON DEMANDリフレッシュのいずれかを使用してリフレッシュできます。

定義問合せの最上位レベルにUNION ALL演算子が含まれ、UNION ALL内の各問合せブロックが、集計を含むマテリアライズド・ビューまたは結合のみを含むマテリアライズド・ビューの要件を満たす場合は、高速リフレッシュがサポートされます。さらに、マテリアライズド・ビューには、各問合せブロック内の個別の値を含む定数列(UNION ALLマーカー)を含める必要があります。これは、次の例では、列1 markerと2 markerです。

UNION ALLを使用したマテリアライズド・ビューの高速リフレッシュに関する制限の詳細は、「UNION ALLを含むマテリアライズド・ビューの高速リフレッシュに関する制限」を参照してください。

UNION ALLを使用するマテリアライズド・ビューの例

次に、UNION ALLを含む、高速リフレッシュ可能なマテリアライズド・ビューの作成例を示します。

例10-5 2つの結合ビューを伴うUNION ALLを使用するマテリアライズド・ビュー

2つの結合ビューを持つUNION ALLマテリアライズド・ビューを作成するには、マテリアライズド・ビュー・ログにROWID列を含める必要があります。次の例で、UNION ALLマーカーは列1 markerと2 markerです。

CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON customers WITH ROWID;

CREATE MATERIALIZED VIEW unionall_sales_cust_joins_mv
REFRESH FAST ON COMMIT
ENABLE QUERY REWRITE AS
(SELECT c.rowid crid, s.rowid srid, c.cust_id, s.amount_sold, 1 marker
FROM sales s, customers c
WHERE s.cust_id = c.cust_id AND c.cust_last_name = 'Smith')
UNION ALL
(SELECT c.rowid crid, s.rowid srid, c.cust_id, s.amount_sold, 2 marker
FROM sales s, customers c
WHERE s.cust_id = c.cust_id AND c.cust_last_name = 'Brown');

例10-6 結合および集計を伴うUNION ALLを使用するマテリアライズド・ビュー

次の例は、結合を含むマテリアライズド・ビューと集計を含むマテリアライズド・ビューのUNION ALLを示します。この例で注意することは2つです。対応するSELECT構文のリストの列のデータ型が一致するようにするには、NULLまたは定数を使用できます。また、UNION ALLマーカー列には文字列リテラルを指定できます。この例では、'Year' umarker、'Quarter' umarkerまたは'Daily' umarkerです。

CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID, SEQUENCE
(amount_sold, time_id)
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON times WITH ROWID, SEQUENCE
(time_id, fiscal_year, fiscal_quarter_number, day_number_in_week)
INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW unionall_sales_mix_mv
REFRESH FAST ON DEMAND AS
(SELECT 'Year' umarker, NULL, NULL, t.fiscal_year,
SUM(s.amount_sold) amt, COUNT(s.amount_sold), COUNT(*)
FROM sales s, times t
WHERE s.time_id = t.time_id
GROUP BY t.fiscal_year)
UNION ALL
(SELECT 'Quarter' umarker, NULL, NULL, t.fiscal_quarter_number,
SUM(s.amount_sold) amt, COUNT(s.amount_sold), COUNT(*)
FROM sales s, times t
WHERE s.time_id = t.time_id and t.fiscal_year = 2001
GROUP BY t.fiscal_quarter_number)
UNION ALL
(SELECT 'Daily' umarker, s.rowid rid, t.rowid rid2, t.day_number_in_week,
s.amount_sold amt, 1, 1
FROM sales s, times t
WHERE s.time_id = t.time_id
AND t.time_id between '01-Jan-01' AND '01-Dec-31');

マテリアライズド・ビューとモデル

マテリアライズド・ビューでは、SQLでの配列ベースの計算を可能にするモデルを使用できます。MODEL句による計算は高コストになる場合があるので、モデル計算用とSELECT ... GROUP BY問合せ用の2つのマテリアライズド・ビューを使用した方がよい場合もあります。たとえば、1つの長いマテリアライズド・ビューを使用するかわりに、次のような2つのマテリアライズド・ビューを作成できます。

CREATE MATERIALIZED VIEW my_groupby_mv
REFRESH FAST
ENABLE QUERY REWRITE AS
SELECT country_name country, prod_name prod, calendar_year year,
SUM(amount_sold) sale, COUNT(amount_sold) cnt, COUNT(*) cntstr
FROM sales, times, customers, countries, products
WHERE sales.time_id = times.time_id AND
sales.prod_id = products.prod_id AND
sales.cust_id = customers.cust_id AND
customers.country_id = countries.country_id
GROUP BY country_name, prod_name, calendar_year;

CREATE MATERIALIZED VIEW my_model_mv
ENABLE QUERY REWRITE AS
SELECT country, prod, year, sale, cnt
FROM my_groupby_mv
MODEL PARTITION BY(country) DIMENSION BY(prod, year)
MEASURES(sale s) IGNORE NAV
(s['Shorts', 2000] =  0.2 * AVG(s)[CV(), year BETWEEN 1996 AND 1999],
s['Kids Pajama', 2000] = 0.5 * AVG(s)[CV(), year BETWEEN 1995 AND 1999],
s['Boys Pajama', 2000] = 0.6 * AVG(s)[CV(), year BETWEEN 1994 AND 1999],
...
<hundreds of other update rules>);

2つのマテリアライズド・ビューを使用することにより、マテリアライズド・ビューmy_groupby_mvを段階的にメンテナンスできるようになります。マテリアライズド・ビューmy_model_mvは、より少量のデータセットに基づいているので(my_groupby_mvをベースに作成されているため)、完全リフレッシュでメンテナンスできます。

モデルを含むマテリアライズド・ビューでは、完全リフレッシュまたはPCTリフレッシュのみを使用できます。また、テキストの部分一致のクエリー・リライトのみを使用できます。

関連項目:
モデル計算の詳細は、第23章「モデリングのSQL」を参照してください。

マテリアライズド・ビューの無効化

マテリアライズド・ビューに関連する依存性は、正しい操作が保証されるように、自動的にメンテナンスされます。あるマテリアライズド・ビューが作成されると、マテリアライズド・ビューはその定義で参照したディテール表に依存します。マテリアライズド・ビューの依存する表に対してINSERT、DELETE、UPDATEなどのDML操作またはDDL操作が行われると、そのマテリアライズド・ビューは無効になります。マテリアライズド・ビューを再検証するには、ALTER MATERIALIZED VIEW COMPILE文を使用します。

マテリアライズド・ビューは、参照されたときに自動的に再検証されます。多くの場合、マテリアライズド・ビューは、正常および透過的に再検証されます。ただし、マテリアライズド・ビューが参照している表の列が削除された場合、またはクエリー・リライト権限を持っていなかったマテリアライズド・ビューの所有者に新たに権限が付与された場合は、次の文を使用してマテリアライズド・ビューを再検証する必要があります。

ALTER MATERIALIZED VIEW mview_name COMPILE;

マテリアライズド・ビューの状態は、データ・ディクショナリ・ビューUSER_MVIEWSまたはALL_MVIEWSを問い合せることでチェックできます。STALENESS列に、そのマテリアライズド・ビューが使用可能かどうかを示すFRESH、STALE、UNUSABLE、UNKNOWN、UNDEFINEDまたはNEEDS_COMPILEのいずれかの値が示されます。状態は自動的にメンテナンスされます。ただし、マテリアライズド・ビューのSTALENESS列がNEEDS_COMPILEとマークされている場合は、ALTER MATERIALIZED VIEW ... COMPILE文を発行してマテリアライズド・ビューを検証し、正しい失効状態にできます。マテリアライズド・ビューの状態がUNUSABLEの場合は、完全リフレッシュを実行して、マテリアライズド・ビューの状態をFRESHに戻す必要があります。リフレッシュしない事前作成表を基にしたマテリアライズド・ビューについては、いったん削除した後、再度作成する必要があります。リモート・マテリアライズド・ビューの失効は追跡されません。そのため、リモート・マテリアライズド・ビューをリライトに使用する場合、これらのビューは信頼できるとみなされます。

マテリアライズド・ビューのセキュリティ問題

独自のスキーマにマテリアライズド・ビューを作成するには、CREATE MATERIALIZED VIEW権限と、別のスキーマにある参照先の表に対するSELECT権限が必要です。別のスキーマにマテリアライズド・ビューを作成するには、CREATE ANY MATERIALIZED VIEW権限が必要です。また、参照先の表が別のスキーマにある場合、マテリアライズド・ビューの所有者には、その表に対するSELECT権限が必要です。さらに、独自のスキーマに含まれない表を参照するマテリアライズド・ビューでクエリー・リライトを有効にするには、GLOBAL QUERY REWRITE権限、または独自のスキーマに含まれない各表に対するQUERY REWRITEオブジェクト権限が必要です。

マテリアライズド・ビューがビルトインされたコンテナ上にあり、作成者が所有者とは異なる場合は、コンテナ表に対するSELECT WITH GRANT権限が必要です。

マテリアライズド・ビューを作成するときに、必要なすべての権限が付与されていると考えられても、権限エラーが継続して発生する場合は、権限が明示的に付与されているのではなく、ロールから権限を継承しようとしている可能性があります。参照される表がそれぞれ異なるスキーマにある場合、マテリアライズド・ビューの所有者には、これらの表へのSELECT権限が明示的に付与されている必要があります。

ON COMMIT REFRESHを指定してマテリアライズド・ビューを作成する場合に、定義問合せにある表が所有者のスキーマに含まれないときは、マテリアライズド・ビューの所有者には追加の権限が必要です。その場合、所有者には、ON COMMIT REFRESHシステム権限、または所有者のスキーマに含まれない各表へのON COMMIT REFRESHオブジェクト権限が必要です。

仮想プライベート・データベース(VPD)を含むマテリアライズド・ビューの問合せ

セキュリティ上の問題はありますが、マテリアライズド・ビューを直接的に問い合せている場合、そのマテリアライズド・ビューは、たまたまマテリアライズド・ビュー化しているビューとして機能します。ビューまたはマテリアライズド・ビューを作成する際、その所有者は、作成しているビューまたはマテリアライズド・ビューの基礎となるベース関係にアクセスするための権限を所有している必要があります。この権限を持つ所有者は、他のユーザーがアクセス可能なビューまたはマテリアライズド・ビューを公開できます(そのビューまたはマテリアライズド・ビューへのアクセス権限がユーザーに付与されている場合)。

仮想プライベート・データベースを含むマテリアライズド・ビューを使用する場合も同様です。マテリアライズド・ビューを作成する際、そのマテリアライズド・ビューの所有者には、マテリアライズド・ビューのベース関係に対して有効になっているVPDポリシーが存在しないようにする必要があります。ただし、マテリアライズド・ビューの所有者が新しいマテリアライズド・ビューにVPDポリシーを設定することは可能です。マテリアライズド・ビューにアクセスするユーザーは、そのマテリアライズド・ビューに設定されているVPDポリシーの対象になります。ただし、これに加えて、マテリアライズド・ビューの基礎となるベース関係のVPDポリシーの対象になることはありません。基礎となるベース関係のセキュリティ処理は、マテリアライズド・ビューの所有者に対して行われます。

仮想プライベート・データベースを含むクエリー・リライトの使用

クエリー・リライトを使用してマテリアライズド・ビューにアクセスする場合、マテリアライズド・ビューは索引のようなアクセス構造体として機能します。つまり、この方法でアクセスされるマテリアライズド・ビューのセキュリティ処理は索引のセキュリティ処理とほとんど同じであり、すべてのセキュリティ・チェックは問合せで指定されている関係に対して実行されるということです。索引やマテリアライズド・ビューを使用するのは、データへのアクセス速度を向上させるためであり、追加のセキュリティ・チェックを提供するためではありません。索引やマテリアライズド・ビューの存在によって、セキュリティ・チェックが追加されることはありません。

これは、VPDが存在する状態でクエリー・リライトを使用してマテリアライズド・ビューにアクセスする場合にも当てはまります。この問合せは、問合せの中で指定されている関係に設定されたすべてのVPDポリシーの対象になります。クエリー・リライトは、ディテール関係にアクセスするのではなくマテリアライズド・ビューを使用するように問合せをリライトしますが、これは、リライトが行われなかったとしてもまったく同じ行が戻されると保証できる場合にかぎられます。つまり、クエリー・リライトは、問合せの中で指定されている関係に対するすべてのVPDポリシーをそのまま尊重する必要があるということです。ただし、マテリアライズド・ビュー自体に対するVPDポリシーは、クエリー・リライトを使用してそのマテリアライズド・ビューにアクセスする場合には影響を及ぼしません。これは、問合せの中で指定されている関係に対するVPDポリシーによって、データはすでに保護されているためです。

マテリアライズド・ビューおよび仮想プライベート・データベースに関する制限

アクティブなVPDポリシーを持つ関係が問合せに含まれている場合、クエリー・リライトは、テキストの完全一致モードおよび部分一致モードを使用しません。ただし、一般的なリライト手法は使用します。これは、VPDが、VPDポリシーに影響を与えるように問合せを透過的に変換するためです。VPDポリシーを伴う問合せに対してクエリー・リライトがテキスト一致の変換を行うとすると、それはVPDポリシーを否定することになります。

また、マテリアライズド・ビューを作成またはリフレッシュする際、そのマテリアライズド・ビューの所有者は、マテリアライズド・ビューのベース関係に対して有効になっているアクティブなVPDポリシーが存在しないようにする必要があります。このようにしない場合、エラーが戻されます。マテリアライズド・ビューの所有者は、このようなVPDポリシーが存在しないようにするか、ポリシーがNULLを戻すようにする必要があります。これは、VPDがマテリアライズド・ビューの定義問合せを透過的に変更して、マテリアライズド・ビューに格納されている行セットと、マテリアライズド・ビュー定義で指定されている行セットが一致しないようにしてしまうためです。

この制限を回避しながら、必要なVPDが指定された行のサブセットを含むマテリアライズド・ビューを作成する方法の1つは、マテリアライズド・ビューのディテール関係に対するアクティブなVPDポリシーを持たないユーザー・アカウントで、マテリアライズド・ビューを作成することです。また、マテリアライズド・ビューのWHERE句に、VPDポリシーの効果を盛り込んだ条件を指定することもできます。クエリー・リライトは、そのVPDポリシーを含む問合せをリライトする際、問合せに含まれるVPD生成の条件と、マテリアライズド・ビューの作成時に直接指定された条件とを照合します。

マテリアライズド・ビューの変更

マテリアライズド・ビューでは、次の6つの変更が可能です。次の操作が可能です。

- リフレッシュ・オプション(FAST/FORCE/COMPLETE/NEVER)の変更

- リフレッシュ・モード(ON COMMIT/ON DEMAND)の変更

- 再コンパイルによる変更

- クエリー・リライトに対する使用可能/使用禁止

- CONSIDER FRESH句による変更

- パーティションのメンテナンス操作

この他の変更は、すべてマテリアライズド・ビューを削除し再作成することで可能になります。

マテリアライズド・ビューが無効化されている場合は、ALTER MATERIALIZED VIEW文のCOMPILE句を使用できます。このコンパイル処理は高速であり、マテリアライズド・ビューがクエリー・リライトに再使用できるようになります。

関連項目:

- ALTER MATERIALIZED VIEW文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

- 「マテリアライズド・ビューの無効化」

11 ディメンション

この章では、データ・ウェアハウスでのディメンションの使用方法について説明します。内容は次のとおりです。

- ディメンションの概要

- ディメンションの作成

- ディメンションの表示

- ディメンションおよび制約の使用

- ディメンションの妥当性チェック

- ディメンションの変更

- ディメンションの削除

ディメンションの概要

ディメンションとは、エンド・ユーザーがビジネス上の質問に答えることができるように、データを分類する構造です。最も一般的なディメンションは、顧客(customers)、製品(products)および時間(times)です。たとえば、衣料品の小売店チェーンの各店舗では、各種衣類の売上と再生利用に関するデータを収集および格納している場合があります。小売店チェーンの管理者は、データ・ウェアハウスを作成して、全店舗にわたる長期間の製品の売上を分析できます。また、次のような質問に答えることができます。

- 1つの製品の宣伝が、宣伝していない関係製品の売上に対してどのような影響があるか

- 宣伝前後の製品の売上はいくらか

- 宣伝が各種物流チャネルにどのように影響するか

小売店のデータ・ウェアハウス・システムのデータには、ディメンションとファクトという2つの重要なコンポーネントがあります。ディメンションは、製品、顧客、宣伝、チャネルおよび時間です。ディメンションを識別する1つの方法は、製品に関するすべての情報を含む製品表や、宣伝に関するすべての情報を含む宣伝表など、参照している表を調べることです。ファクトは、売上(売上数量)および利益です。データ・ウェアハウスには、1日ごとの各製品の売上に関するファクトが含まれます。

このようなデータ・ウェアハウスの典型的なリレーショナル実装が、スター・スキーマです。ファクト情報はファクト表に格納され、ディメンション情報はディメンション表に格納されます。たとえば、それぞれの売上トランザクション・レコードは、顧客別、製品別、販売チャネル別、宣伝別、および日付(時間)別に一意に定義されます。

参照:
詳細は、第20章「スキーマのモデリング化技法」を参照してください。

Oracle Databaseでは、ディメンション情報自体がディメンション表に格納されます。また、データベース・オブジェクト・ディメンションにより、ディメンション情報を階層形式に編成してグループ化できます。これは、制約では表すことのできない列間または列グループ(階層レベル)間の1:nの関係を表します。階層内でのレベルを上げることはデータのロールアップと呼ばれ、レベルを下げることはデータのドリルダウンと呼ばれます。小売店の例では、次のようになります。

- 時間ディメンションでは、月は四半期に、四半期は年に、年は全年にそれぞれロールアップされます。

- 製品ディメンションでは、製品はサブカテゴリに、サブカテゴリはカテゴリに、カテゴリは全製品にそれぞれロールアップされます。

- 顧客ディメンション内では、顧客は市にロールアップされます。市は州にロールアップされます。州は国にロールアップされます。国は地区にロールアップされます。最後に、地区は地域にロールアップされます。これを図11-1に示します。

図11-1 顧客ディメンションのロールアップの例

通常、データ分析は、ディメンション階層の上位レベルから始まり、この分析が正当化されると順にドリルダウンします。

ディメンションを定義する必要はありません。ただし、アプリケーションでディメンショナル・モデリングを使用する場合は、ディメンションを注意深く作成すると、クエリー・リライトで複雑なタイプのリライトを実行できるようになり、大きなメリットが得られます。また、ディメンションは、マテリアライズド・ビューの特定のタイプのリフレッシュ操作およびSQLアクセス・アドバイザに対してもメリットをもたらします。ディメンションが必須になるのは、SQLアクセス・アドバイザ(マテリアライズド・ビューおよび索引管理用のGUIツール)を使用して、ワークロードを発生させることなく、作成、削除または保持すべきマテリアライズド・ビューおよび索引を推奨させる場合のみです。

関連項目:

- クエリー・リライトの詳細は、第18章「基本的なクエリー・リライト」を参照してください。

- SQLアクセス・アドバイザの詳細は、『Oracle Databaseパフォーマンス・チューニング・ガイド』を参照してください。

この章で説明するディメンションの関係を完全に満たさないスキーマには、メリットの有無に関係なく、ディメンションを作成しないでください。作成すると、問合せで不適切な結果が戻される場合があります。

ディメンションの作成

ディメンション・オブジェクトを作成する前に、ディメンション・データを含み得るディメンション表が、データベース内に存在する必要があります。たとえば、顧客ディメンションを作成する場合は、市、州および国の情報を含む1つ以上の表が存在する必要があります。スター・スキーマ・データ・ウェアハウスには、これらのディメンション表がすでに存在しています。したがって、どれが使用されるかを簡単に識別できます。

図11-1のようなディメンションの階層を定義できます。たとえば、市は州および国の子です(市レベルのデータは州レベルまで集計できるため)。この階層情報は、データベース・オブジェクト・ディメンションに格納されます。

複数の表に格納されているディメンションが正規化、もしくは部分的に正規化されている場合、これらの表がどのように結合されているか把握する必要があります。ディメンション表同士の結合においては、親表の各行と子表の各行の間に1対多の関係が保証されているかどうかに注意してください。また非正規化ディメンションの場合、子の列は親(または属性)の列を一意に決定できなくてはなりません。制約を使用してこれらの関係を表す場合、制約によって表される関係が他の手段で保証されるのであれば、NOVALIDATE句およびRELY句を使用して制約を使用可能にできます。

ディメンションのNULLのレベルは必要に応じてスキップできます。たとえば、プエルトリコの例を考えてみます。プエルトリコを北アメリカ地域に含めたいが、州のカテゴリには含めたくないとします。これを実行するには、SKIP WHEN NULL句を使用します。詳細は、この章の後半に記載されているサンプル・ディメンションを参照してください。構文および制限については、『Oracle Database SQL言語リファレンス』を参照してください。

ディメンションを作成するには、CREATE DIMENSION文またはOracle Enterprise Managerのディメンション・ウィザードのいずれかを使用します。CREATE DIMENSION文中では、LEVEL句を使用してディメンション・レベルの名前を指定します。

関連項目:
CREATE DIMENSION文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

図11-1に示すとおり、この顧客ディメンションには地理的にロールアップする階層が1つあります(図では子レベルと親レベルの間に矢印が描かれています)。

この図式内の各矢印は、すべての子に対して親が1つのみあることを示します。たとえば、各市は1つの州のみに含まれる必要があり、各州は1つの国のみに含まれる必要があります。2つ以上の国に属する州は、階層の整合性に違反します。また、州に属さない市(ワシントンD.C.など)を含めたい場合は、SKIP WHEN NULL句を使用する必要があります。階層の整合性は、集計を含むマテリアライズド・ビューに対する管理機能を正確に操作するために必要です。

たとえば、product、subcategoryおよびcategoryのレベルを含むディメンションproducts_dimを宣言できます。

CREATE DIMENSION products_dim
LEVEL product           IS (products.prod_id)
LEVEL subcategory       IS (products.prod_subcategory)
LEVEL category          IS (products.prod_category) ...

ディメンション内の各レベルは、データベースにある表の1つ以上の列に対応付けられている必要があります。したがって、レベルproductはproducts表の列prod_idで識別され、レベルsubcategoryは同じ表の列prod_subcategoryで識別されます。

この例では、データベース表は非正規化され、すべての列は同じ表に存在します。ただし、これはディメンション作成の前提条件ではありません。JOIN KEY句を使用した、正規化スキーマ設計を持つディメンションcustomers_dimの作成方法については、「正規化ディメンション表の使用」を参照してください。

次のステップでは、HIERARCHY文でレベル間の関係を宣言し、階層に名前を付けます。階層関係とは、階層内の1つのレベルから次のレベルに対する機能的な依存関係です。前に定義したレベルの名前を使用して、CHILD OF関係によって、各子のレベル値が1つのみの親レベル値に対応付けられていることが示されます。次の文では、prod_rollupという階層を宣言し、product、subcategoryおよびcategory間の関係を定義しています。

HIERARCHY prod_rollup
(product         CHILD OF
subcategory     CHILD OF
category)

1:nの階層関係に加えて、ディメンションには、階層レベルとその依存ディメンション属性との間の1:1の属性関係も含まれます。たとえば、『Oracle Databaseサンプル・スキーマ』で定義されているように、ディメンションtimes_dimにはfiscal_month_desc、fiscal_month_nameおよびdays_in_fiscal_monthの各列があります。この関係の定義は、次のとおりです。

LEVEL fis_month IS TIMES.FISCAL_MONTH_DESC
...
ATTRIBUTE fis_month DETERMINES
(fiscal_month_name, days_in_fiscal_month)

ATTRIBUTE ... DETERMINES句は、fis_monthをfiscal_month_nameとdays_in_fiscal_monthに関係付けます。これが単方向の依存関係であることに注意してください。保証されているのは、特定のfiscal_month(1999-11など)について、fiscal_month_nameおよびdays_in_fiscal_monthに一致する値がそれぞれ1つ(Novemberおよび28など)のみであることです。各会計年度のNovemberであるfiscal_month_nameに基づいて特定のfiscal_month_descを判断することはできません。

この例では、fiscal_month_descではなくfiscal_month_nameで問合せが発行されたとします。この1:1の関係は属性とレベルの間に存在するため、fiscal_month_descを含んでいる集計済のマテリアライズド・ビューをディメンション情報に後戻り結合し、データの識別に使用できます。

関連項目:
ディメンション情報の使用方法の詳細は、第18章「基本的なクエリー・リライト」を参照してください。

ディメンション定義の例を次に示します。

CREATE DIMENSION products_dim
LEVEL product           IS (products.prod_id)
LEVEL subcategory       IS (products.prod_subcategory) [SKIP WHEN NULL]
LEVEL category          IS (products.prod_category)
HIERARCHY prod_rollup (
product         CHILD OF
subcategory     CHILD OF
category)
ATTRIBUTE product DETERMINES
(products.prod_name, products.prod_desc,
prod_weight_class, prod_unit_of_measure,
prod_pack_size, prod_status, prod_list_price, prod_min_price)
ATTRIBUTE subcategory DETERMINES
(prod_subcategory, prod_subcategory_desc)
ATTRIBUTE category DETERMINES
(prod_category, prod_category_desc);

または、次の例に示すように、attribute_clauseのかわりにextended_attribute_clauseを使用することもできます。

CREATE DIMENSION products_dim
LEVEL product           IS (products.prod_id)
LEVEL subcategory       IS (products.prod_subcategory)
LEVEL category          IS (products.prod_category)
HIERARCHY prod_rollup (
product         CHILD OF
subcategory     CHILD OF
category
)
ATTRIBUTE product_info LEVEL product DETERMINES
(products.prod_name, products.prod_desc,
prod_weight_class, prod_unit_of_measure,
prod_pack_size, prod_status, prod_list_price, prod_min_price)
ATTRIBUTE subcategory DETERMINES
(prod_subcategory, prod_subcategory_desc)
ATTRIBUTE category DETERMINES
(prod_category, prod_category_desc);

ディメンションの設計、作成およびメンテナンスは、データ・ウェアハウス・スキーマの設計、作成およびメンテナンスの一部です。ディメンションを作成したら、次の要件が満たされているかどうかを検証してください。

- 親と子の間に、1:nの関係がある必要があります。親は、1つ以上の子を持つことができますが、子は1つの親しか持てません。

- 階層レベルとその依存ディメンション属性の間に、1:1の属性関係がある必要があります。たとえば、fiscal_month_desc列がある場合、可能な属性関係はfiscal_month_desc対fiscal_month_nameになります。スキップ・レベルにある関係の行にNULL値を持つレベル列がある場合にNULLレベルをスキップするには、その行にもNULL値を持つ属性関係の列がなければなりません。

- 親レベルと子レベルの列が異なる表に存在する場合、これらの間の結合も1:nの結合関係がある必要があります。SKIP WHEN NULL句を使用しない場合、子表の各行は、親表の1つの行のみと結合している必要があります。この関係は、子の結合キーがNULLでないこと、子の結合キーと親の結合キーの参照整合性が保持されること、および親の結合キーは一意であることが必要であるため、参照整合性のみより強力です。

- SKIP WHEN NULL句を使用しない場合は、各階層レベルの列がNULLでないこと、および階層の整合性が保たれていることを確認する必要があります(必要に応じて、データベース制約を使用してください)。

- オプションの結合キーは、階層内のスキップ・レベル(存在する場合)にあるスキップ対象外の子であるCHILDLEVと、そのスキップ・レベル(同様に、存在する場合)にあるスキップ対象外の最も近い祖先であるANCLEVを接続する結合キーです。また、この結合キーは、異なる関係にわたってCHILDLEVおよびANCLEVが定義されている場合にのみ使用可能です。

- ディメンションの階層は、相互にオーバーラップすることも切り離されることもあります。ただし、階層レベルの列を2つ以上のディメンションに関連付けることはできません。

- ディメンションの図式内で循環を形成する階層の定義はサポートされません。たとえば、階層レベルは、それ自体とは直接的にも間接的にもつなげることはできません。

注意:
ディメンション・オブジェクトに格納される情報は宣言のみです。前述の依存関係は、ディメンション・オブジェクトの作成のみでは施行されません。「ディメンションの妥当性チェック」で説明するように、すべてのディメンション定義はDBMS_DIMENSION.VALIDATE_DIMENSIONプロシージャで検証する必要があります。

属性列の削除および作成

CREATE DIMENSION文でATTRIBUTE句を使用すると、階層レベルによって一意に決定される1つ以上の列を指定できます。

extended_attribute_clauseを使用して、階層レベルによって決定される複数の列を作成する場合は、すべての属性列を削除することなく1つの属性列を削除できます。また、CREATEまたはALTER DIMENSION文で各ATTRIBUTE句に属性名を指定すると、ATTRIBUTE句ごとに属性名が付けられ、複数のレベル-列関係を個々に指定できるようになります。

次に、すべての列を削除することなく1つの列を削除できるようにする文を示します。

CREATE DIMENSION products_dim
LEVEL product         IS (products.prod_id)
LEVEL subcategory     IS (products.prod_subcategory)
LEVEL category        IS (products.prod_category)
HIERARCHY prod_rollup (
product        CHILD OF
subcategory    CHILD OF category)
ATTRIBUTE product DETERMINES
(products.prod_name, products.prod_desc,
prod_weight_class, prod_unit_of_measure,
prod_pack_size,prod_status, prod_list_price, prod_min_price)
ATTRIBUTE subcategory_att DETERMINES
(prod_subcategory, prod_subcategory_desc)
ATTRIBUTE category DETERMINES
(prod_category, prod_category_desc);

ALTER DIMENSION products_dim
DROP ATTRIBUTE subcategory_att LEVEL subcategory COLUMN prod_subcategory;

関連項目:
CREATE DIMENSION文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

複数の階層

単一のディメンション定義に、複数の階層を含めることができます。小売店で、ある品目の売上を長期間追跡すると想定します。最初のステップは、売上が追跡される時間の時間ディメンションを定義することです。図11-2に、2つの時間階層を持つディメンションtimes_dimを示します。

図11-2 2つの時間階層を持つtimes_dimディメンション

この図から、次のCREATE DIMENSION文によって定義された非正規化time_dimディメンションの階層を作成できます。完全なCREATE DIMENSION文およびCREATE TABLE文は、『Oracle Databaseサンプル・スキーマ』を参照してください。

CREATE DIMENSION times_dim
LEVEL day         IS times.time_id
LEVEL month       IS times.calendar_month_desc
LEVEL quarter     IS times.calendar_quarter_desc
LEVEL year        IS times.calendar_year
LEVEL fis_week    IS times.week_ending_day
LEVEL fis_month   IS times.fiscal_month_desc
LEVEL fis_quarter IS times.fiscal_quarter_desc
LEVEL fis_year    IS times.fiscal_year
HIERARCHY cal_rollup    (
day     CHILD OF
month   CHILD OF
quarter CHILD OF
year
)
HIERARCHY fis_rollup    (
day         CHILD OF
fis_week    CHILD OF
fis_month   CHILD OF
fis_quarter CHILD OF
fis_year
) <attribute determination clauses>;

正規化ディメンション表の使用

ディメンションの定義に使用される表は、正規化または非正規化されている場合があります。また、個々の階層は、正規化または非正規化できます。1つの階層のレベルが同じ表のものである場合は、完全な非正規化階層と呼ばれます。たとえば、times_dimディメンション内のcal_rollupは、非正規化階層です。1つの階層のレベルが異なる表に存在する場合、このような階層は、完全なまたは部分的な正規化階層です。この項では、正規化階層の定義方法を示します。

顧客の所在地が市、州および国別に追跡されているとします。このデータは、customers表およびcountries表に格納されます。データ・エンティティcust_idおよびcountry_idが別々の表から取り出されるため、顧客ディメンションcustomers_dimは部分的に正規化されます。ディメンション定義内のJOIN KEY句では、階層内のレベルの結合方法が指定されます。ディメンションの文の一部を次に示します。完全なCREATE DIMENSION文およびCREATE TABLE文は、『Oracle Databaseサンプル・スキーマ』を参照してください。

CREATE DIMENSION customers_dim
LEVEL customer  IS (customers.cust_id)
LEVEL city      IS (customers.cust_city)
LEVEL state     IS (customers.cust_state_province)
LEVEL country   IS (countries.country_id)
LEVEL subregion IS (countries.country_subregion)
LEVEL region IS (countries.country_region)
HIERARCHY geog_rollup (
customer        CHILD OF
city            CHILD OF
state           CHILD OF
country         CHILD OF
subregion       CHILD OF
region
JOIN KEY (customers.country_id) REFERENCES country);

SKIP WHEN NULL句を使用する場合、JOIN KEY句を使用して階層内に存在しないレベルを持つレベル同士をリンクできます。たとえば、次の文によって、SKIP WHEN NULLと宣言された州レベルを市および国に結合できます。

JOIN KEY (city.country_id) REFERENCES country;

これにより、顧客レベルおよび市レベルの行が、国レベル、地区レベルおよび地域レベルの行に関連付けられます。

ディメンションの表示

ディメンションは、次のいずれかの方法で参照できます。

- Oracle Enterprise Managerの使用

- DESCRIBE_DIMENSIONプロシージャの使用

Oracle Enterprise Managerの使用

Oracle Enterprise Managerを使用すると、データ・ウェアハウス内にあるすべてのディメンションを表示できます。すべてのディメンションを表示するには、「スキーマ」アイコンから「ディメンション」オブジェクトを選択します。特定のディメンションを選択すると、その定義された階層、レベルおよび属性がグラフィカルに表示されます。

DESCRIBE_DIMENSIONプロシージャの使用

ディメンションの定義を表示するには、DBMS_DIMENSIONパッケージのDESCRIBE_DIMENSIONプロシージャを使用します。たとえば、ディメンションが次の文でshサンプル・スキーマに作成されているとします。

CREATE DIMENSION channels_dim
LEVEL channel       IS (channels.channel_id)
LEVEL channel_class IS (channels.channel_class)
HIERARCHY channel_rollup (
channel CHILD OF channel_class)
ATTRIBUTE channel DETERMINES (channel_desc)
ATTRIBUTE channel_class DETERMINES (channel_class);

次のようにしてDESCRIBE_DIMENSIONプロシージャを実行します。

SET SERVEROUTPUT ON FORMAT WRAPPED;  --to improve the display of info
EXECUTE DBMS_DIMENSION.DESCRIBE_DIMENSION('SH.CHANNELS_DIM');

結果が次のように出力されます。

EXECUTE DBMS_DIMENSION.DESCRIBE_DIMENSION('SH.CHANNELS_DIM');
DIMENSION SH.CHANNELS_DIM
LEVEL CHANNEL IS SH.CHANNELS.CHANNEL_ID
LEVEL CHANNEL_CLASS IS SH.CHANNELS.CHANNEL_CLASS

HIERARCHY CHANNEL_ROLLUP (
CHANNEL CHILD OF
CHANNEL_CLASS)

ATTRIBUTE CHANNEL LEVEL CHANNEL DETERMINES
SH.CHANNELS.CHANNEL_DESC
ATTRIBUTE CHANNEL_CLASS LEVEL CHANNEL_CLASS DETERMINES
SH.CHANNELS.CHANNEL_CLASS

ディメンションおよび制約の使用

制約は、ディメンションに対して重要な役割を果します。データ・ウェアハウスでは、完全な参照整合性が有効化されますが、常にそうである必要はありません。これは、通常、業務系データベースは完全な参照整合性を持っており、データ・ウェアハウスに送られるデータがすでに設定されている整合性ルールに違反しないことを保証できるためです。

制約を使用可能にし、妥当性チェックの時間を考慮する必要がある場合に、次のようにNOVALIDATE句を使用することをお薦めします。

ENABLE NOVALIDATE CONSTRAINT pk_time;

主キーおよび外部キーも実装する必要があります。ファクト表の参照整合性制約およびNOT NULL制約によって、マテリアライズド・ビューの有用性を拡張するクエリー・リライトの機能で利用する情報が提供されます。

また、次のようにRELY句を使用して、制約が正しいことをクエリー・リライトに提示する必要があります。

ALTER TABLE time MODIFY CONSTRAINT pk_time RELY;

この情報は、クエリー・リライトにも使用されます。詳細は、第18章「基本的なクエリー・リライト」を参照してください。

SKIP WHEN NULL句を使用する場合、NOT NULL制約を持たない参照レベル列が少なくとも1つ必要です。

ディメンションの妥当性チェック

ディメンション・オブジェクトの情報は宣言のみで、データベースでは規定されません。ディメンションによって表された関係が不適切な場合は、不適切な結果が戻される可能性があります。したがって、DBMS_DIMENSION.VALIDATE_DIMENSIONプロシージャを定期的に使用して、CREATE DIMENSIONで指定される関係を検証する必要があります。

このプロシージャは使いやすく、指定するパラメータは次の4つのみです。

- dimension: 所有者と名前。

- incremental: このディメンションの表の新しい行のみをチェックするにはTRUEに設定。

- check_nulls: SKIP WHEN NULL句を含むレベルにないすべての列がNULLではないことを検証するにはTRUEに設定。

- statement_id: プロシージャの各実行結果を識別するためにユーザーが指定する一意の識別子。

次の例では、shスキーマ内のTIME_FNディメンションの妥当性チェックが行われます。

@utldim.sql
EXECUTE DBMS_DIMENSION.VALIDATE_DIMENSION ('SH.TIME_FN', FALSE, TRUE,
'my first example');

VALIDATE_DIMENSIONプロシージャを実行する前に、提供されているスクリプトutldim.sqlを実行して、ローカル表DIMENSION_EXCEPTIONSを作成しておく必要があります。VALIDATE_DIMENSIONプロシージャでエラーが検出されると、この表に書き込まれます。この表を問い合せると、検出されたエラーを識別できます。次に例を示します。

SELECT * FROM dimension_exceptions
WHERE statement_id = 'my first example';

STATEMENT_ID       OWNER  TABLE_NAME  DIMENSION_NAME   RELATIONSHIP   BAD_ROWID
------------       -----  ----------  --------------   ------------   ---------
my first example   SH     MONTH       TIME_FN          FOREIGN KEY    AAAAuwAAJAAAARwAAA

ただし、この表を問い合せるより、無効な行のROWIDを使用して、制約に違反する実際の行を取り出す問合せの方が適している場合があります。この例では、TIME_FNディメンションが、month表をチェックしています。これにより、制約に違反する行が検出されています。ROWIDを使用すると、次のように、month表のうち問題の原因となっている行を正確に確認できます。

SELECT * FROM month
WHERE rowid IN (SELECT bad_rowid
FROM dimension_exceptions
WHERE statement_id = 'my first example');

MONTH    QUARTER   FISCAL_QTR    YEAR    FULL_MONTH_NAME    MONTH_NUMB
------   -------   ----------    ----    ---------------    ----------
199903     19981        19981    1998              March             3

ディメンションの変更

ALTER DIMENSION文を使用すると、ディメンションを変更できます。このコマンドを使用して、レベル、階層または属性をディメンションに対して追加または削除できます。

図11-2の時間ディメンションの場合では、属性fis_year、階層fis_rollupまたはレベルfiscal_yearを削除できます。また、次のようにf_yearという新しいレベルを追加できます。

ALTER DIMENSION times_dim DROP ATTRIBUTE fis_year;
ALTER DIMENSION times_dim DROP HIERARCHY fis_rollup;
ALTER DIMENSION times_dim DROP LEVEL fis_year;
ALTER DIMENSION times_dim ADD LEVEL f_year IS times.fiscal_year;

ディメンションの作成時にextended_attribute_clauseを使用した場合は、すべての属性列を削除することなく1つの属性列を削除できます。これは、「属性列の削除および作成」に示されているように、次のような文で行うことができます。

ALTER DIMENSION product_dim
DROP ATTRIBUTE size LEVEL prod_type COLUMN Prod_TypeSize;

ディメンション内でさらに依存性を持つオブジェクトの削除を試みると、そのディメンションの変更はOracle Databaseにより拒否されます。ディメンションが参照しているスキーマ・オブジェクトのいずれかを変更した場合、そのディメンションは無効になります。たとえば、ディメンションが定義されている表が変更されると、そのディメンションは無効になります。

次の文のように、SKIP WHEN NULL句を含むレベルを追加することによってディメンションを変更できます。

ALTER DIMENSION times_dim
ADD LEVEL f_year IS times.fiscal_year SKIP WHEN NULL;

ただし、SKIP WHEN NULL句を含むレベルは変更できません。かわりに、そのレベルを削除して再作成する必要があります。

ディメンションの状態をチェックするには、ALL_DIMENSIONSデータ・ディクショナリ・ビューにあるinvalid列の内容を参照します。ディメンションを再検証するには、次のようにCOMPILEオプションを使用します。

ALTER DIMENSION times_dim COMPILE;

ディメンションは、Oracle Enterprise Managerを使用しても変更または削除できます。

ディメンションの削除

ディメンションは、DROP DIMENSION文を使用して削除できます。たとえば、次のような文を発行します。

DROP DIMENSION times_dim;

第IV部

データ・ウェアハウス環境の管理

この部では、データ・ウェアハウスの管理に必要な作業について説明します。

次の章で構成されています。

- 第12章「抽出、変換、ロードの概要」

- 第13章「データ・ウェアハウスにおける抽出」

- 第14章「データ・ウェアハウスにおける転送」

- 第15章「ロードおよび変換」

- 第16章「データ・ウェアハウスのメンテナンス」

- 第17章「チェンジ・データ・キャプチャ」

12 抽出、変換、ロードの概要

この章では、データ・ウェアハウス環境でのデータの抽出、転送、変換およびロード・プロセスについて説明します。含まれる内容は、次のとおりです。

- データ・ウェアハウスにおけるETLの概要

- データ・ウェアハウスのETLツール

データ・ウェアハウスにおけるETLの概要

データ・ウェアハウスの目的はビジネス分析ですが、そのためには、定期的にデータをウェアハウスにロードする必要があります。このロードを行うには、1つ以上の業務系システムからデータを抽出し、データ・ウェアハウスにコピーする必要があります。データ・ウェアハウス環境では、多数のシステムにわたる大量のデータを統合、再配置および連結し、結果として、統一された新たな情報ベースをビジネス・インテリジェンスに提供することが課題になります。

ソース・システムからデータを抽出し、データ・ウェアハウスに取り込むプロセスは、一般的にETLと呼ばれます。これは、抽出(Extraction)、変換(Transformation)およびロード(Loading)の略です。ETLは1つの広範囲なプロセスであり、明確に定義された3つのステップの組合せではありません。ETLという略語は簡略すぎるように見えます。これでは、転送フェーズが示されておらず、他の3つのフェーズがそれぞれ独立しているかのような印象を受けます。いずれにせよ、このプロセス全体はETLとして知られています。

ETLの方法論およびタスクは以前から周知されており、必ずしもデータ・ウェアハウス環境に固有ではありません。あらゆる企業において、様々な独自のアプリケーションやデータベース・システムが、ITのバックボーンとなっています。データをアプリケーション間やシステム間で共有し、統合し、少なくとも2つのアプリケーションに同じ情報が表示されるようにする必要があります。データの共有のほとんどは、ETLのようなメカニズムで対処されています。

データ・ウェアハウスにおけるETLの基本

ETLプロセス中に発生する処理を考えてみます。このプロセスで主に発生する処理には、次のタスクがあります。

データの抽出

抽出中には、必要なデータが識別され、データベース・システムやアプリケーションなど、多数の異なるソースから抽出されます。通常、必要なデータの特定のサブセットは識別できないため、必要以上のデータを抽出する必要があり、関連データの識別は後の時点で行われます。ソース・システムの機能(オペレーティング・システムのリソースなど)によっては、この抽出プロセス中になんらかの変換が行われる場合もあります。抽出されるデータのサイズは、ソース・システムとビジネス状況に応じて数百KBから数GBになります。これは、2つの(論理的に)同一の抽出処理間の時間デルタにもあてはまります。タイム・スパンは、日数/時間数、分数やほぼリアルタイムまで様々です。たとえば、Webサーバーのログ・ファイルは、きわめて短期間のうちに数百MBにまで大きくなることがあります。

データの転送

データの抽出後は、それをターゲット・システムまたは中間システムに物理的に転送し、さらに処理する必要があります。選択した転送方法によっては、このプロセス中にもなんらかの変換を行うことができます。たとえば、ゲートウェイを通じてリモート・ターゲットに直接アクセスするSQL文では、SELECT文の中で2つの列を連結できます。

この項では、スケーラビリティに重点を置いた例を示します。Oracle Databaseを長く使用していれば、PL/SQLによる複雑なデータ変換ロジックのプログラミングについてはエキスパートであると言えます。ここでは、このような多数のデータ操作の代替手段を提案し、Oracleの新しいSQL機能、特にETLとパラレル問合せインフラストラクチャを活用する実装に重点を置いて説明します。

データ・ウェアハウスのETLツール

ETLプロセスの設計およびメンテナンスは、データ・ウェアハウス・プロジェクトで最も困難でリソースを大量に必要とする部分だと考えられています。多数のデータ・ウェアハウス・プロジェクトでは、このプロセスを管理するためにETLツールが使用されます。たとえば、Oracle Warehouse BuilderにはETL機能が搭載されており、本来のデータベース機能を活用できます。他のデータ・ウェアハウス・ビルダーもありますが、これらは独自のETLツールおよびプロセスをデータベース内またはデータベース外に作成します。

ETLをデータ・ウェアハウスの日常操作および今後の強化のサポートの一部として順調に実装するには、抽出、変換およびロードのサポートの他にも重要なタスクがあります。通常、これらのタスクは、データ・ウェアハウス設計およびデータ・フロー設計のサポートとともに、Oracle Warehouse BuilderなどのETLツールで取り扱われます。

OracleはETLツールではなく、ETLの完全なソリューションを提供するものでもありません。ただし、Oracleには、ETLツールとカスタマイズされたETLソリューションの両方で使用できる豊富な機能が搭載されています。Oracleは、Oracleデータベース間でデータを転送し、大量のデータを変換し、新しいデータをデータ・ウェアハウスに迅速にロードするための技術を提供します。

データ・ウェアハウスにおける日次操作

継続的なロードおよび変換を、特定の順序でスケジュールして処理する必要があります。操作またはその一部の成否に応じて結果を追跡する必要があります。また、場合によってはそれ以降の代替プロセスを開始できます。操作の進行状況の制御とビジネス・ワークフローの定義は、通常はOracle Warehouse BuilderなどのETLツールで扱われます。

データ・ウェアハウスの発展

データ・ウェアハウスは生きたITシステムであり、ソースとターゲットが変化することがあります。このような変化は、古いETLプロセス・フロー情報を上書きまたは削除せずに、システムの稼働期間中を通じてメンテナンスし、追跡する必要があります。ウェアハウス内の情報の信頼性のレベルを築き、維持するために、ウェアハウス内の個々のレコードの処理フローは、将来の適切な時期に、いつでも再構築が可能です。

13 データ・ウェアハウスにおける抽出

この章では、抽出について説明します。抽出とは、業務系システムからデータを取り出してデータ・ウェアハウスまたはステージング・システムに移動するプロセスです。内容は次のとおりです。

- データ・ウェアハウスにおける抽出の概要

- データ・ウェアハウスにおける抽出方法の概要

- データ・ウェアハウスにおける抽出の例

データ・ウェアハウスにおける抽出の概要

抽出とは、データ・ウェアハウス環境で使用できるように、ソース・システムからデータを抽出する操作です。これは、ETLプロセスの最初のステップです。抽出後のデータは、変換してデータ・ウェアハウスにロードできます。

通常、データ・ウェアハウスのソース・システムになるのは、トランザクション処理データベース・アプリケーションです。たとえば、販売分析データ・ウェアハウスのソース・システムの例としては、現在の受注状況をすべて記録する注文入力システムがあります。

抽出プロセスの設計および作成は、通常はETLプロセスおよびデータ・ウェアハウスのプロセス全体の中で、最も時間がかかる作業の1つです。ソース・システムが非常に複雑で、詳細にドキュメント化されていないため、どのデータを抽出する必要があるかを決定できない場合もあります。通常、データは1度だけ抽出されるのではなく、変更があったすべてのデータをデータ・ウェアハウスに提供して最新の状態に保つために、定期的に何回も抽出されます。さらに、データ・ウェアハウスの抽出プロセスの必要性に応えようとしても、通常ソース・システムは変更できず、そのパフォーマンスまたは可用性を調整することもできません。

これらは、抽出およびETL全体で考慮する必要がある重要な考慮点です。ただし、この章では、各種のソースおよび抽出方法に関する技術的な考慮点について重点的に説明します。ここでは、データ・ウェアハウス・チームが抽出対象のデータをすでに識別していることを前提に、ソース・データベースからデータを抽出する一般的なテクニックについて説明します。

このプロセスの設計では、主に次の2つのカテゴリに関する意思決定を行うことになります。

- どの抽出方法を選択するか
これは、ソース・システム、転送プロセスおよびウェアハウスのリフレッシュの所要時間に影響します。

- 抽出されたデータを後続の処理にどんな方法で提供するか
これは、転送方法と、データのクレンジングおよび変換のニーズに影響します。

データ・ウェアハウスにおける抽出方法の概要

どの抽出方法を選択する必要があるかは、ソース・システムに大きく左右され、ターゲットとなるデータ・ウェアハウス環境でのビジネス・ニーズも考慮する必要があります。処理負荷の増大やパフォーマンスへの影響を考えると、データの増分抽出機能を強化するために新たなロジックをソース・システムに追加するというのはあまり現実的な方法ではありません。顧客には既製のアプリケーション・システムに対する追加が許されていない場合さえもあります。

論理的抽出方法

論理的抽出には、次の2種類があります。

- 全体抽出

- 増分抽出

全体抽出

データ全体がソース・システムから抽出されます。この抽出では、ソース・システムで現在使用可能なデータがすべて反映されるため、前回の抽出成功以降にデータソースに対して行われた変更を追跡する必要がありません。ソース・データはそのまま提供され、ソース側では追加の論理情報(タイムスタンプなど)は不要です。全体抽出の例には、特定の表のエクスポート・ファイルや、ソース表全体をスキャンするリモートSQL文があります。

増分抽出

特定の時点で、履歴で適切に定義されたイベント以降に変更があったデータのみが抽出されます。このイベントは、最終抽出時刻でも、会計期間の最終記帳日のように複雑なビジネス・イベントでもかまいません。このデルタ変更を識別するには、この特定の時間事象以降に変更があった情報をすべて識別する機能が必要です。この情報は、最終変更日時のタイムスタンプを反映するアプリケーション列のようなソース・データ自体、または適切な追加メカニズムにより起点となったトランザクションに加え、変更が追跡されるチェンジ・テーブルから得ることができます。ほとんどの場合は、後者の方法を使用すると、ソース・システムに抽出のロジックを追加することになります。

抽出プロセスに対して、なんらかの形で変更をキャプチャするテクニックを使用しているデータ・ウェアハウスはあまりありません。そのかわり、ソース・システムからすべての表をデータ・ウェアハウスまたはステージング・エリアに抽出し、ソース・システムからの前回の抽出と比較して変更データを識別します。この方法では、ソース・システムが重大な影響を受けることはありませんが、データ・ボリュームが非常に大きい場合は、特に、データ・ウェアハウスの処理に対しては、多大な負荷になります。

Oracleのチェンジ・データ・キャプチャ(CDC)のメカニズムでは、このような差分情報を抽出してメンテナンスできます。チェンジ・データ・キャプチャ・フレームワークの詳細は、第17章「チェンジ・データ・キャプチャ」を参照してください。

物理的抽出方法

選択した論理的抽出方法と、ソース側の機能と制限に応じて、抽出データを2つのメカニズムで物理的に抽出できます。つまり、データをソース・システムからオンラインで抽出する方法と、オフライン構成から抽出する方法があります。オフライン構成は既存のものを使用する場合と、抽出ルーチンで生成する場合があります。

物理的抽出には、次の2つの方法があります。

- オンライン抽出

- オフライン抽出

オンライン抽出

データはソース・システム自体から直接抽出されます。抽出プロセスでは、ソース・システムに直接接続してソース表自体にアクセスするか、データが事前構成済の方法(スナップショット・ログやチェンジ・テーブルなど)で格納されている中間システムに接続できます。なお、中間システムは必ずしもソース・システムと物理的に異なるとは限りません。

オンライン抽出では、分散トランザクションでオリジナルのソース・オブジェクトを使用しているか、準備したソース・オブジェクトを使用しているかを考慮する必要があります。

オフライン抽出

データはソース・システムから直接抽出されるのではなく、オリジナル・ソース・システム外部で明示的にステージングされます。データはすでに既存の仕組み(REDOログ、アーカイブ・ログまたはトランスポータブル表領域など)を持っているか、または抽出ルーチンにより作成されています。

次の仕組みを考慮する必要があります。

- フラット・ファイル
定義済の汎用フォーマットのデータ。さらに処理するには、ソース・オブジェクトに関する追加情報が必要です。

- ダンプ・ファイル
Oracle固有のフォーマット。選択したユーティリティによって、含んでいるオブジェクトに関する情報がある場合とない場合があります。

- REDOログおよびアーカイブ・ログ
情報は特殊な追加ダンプ・ファイルにあります。

- トランスポータブル表領域
Oracleデータベース間で大量のデータを抽出および移動する強力な手段。この機能を使用して、データの抽出および転送を行う例については、第14章「データ・ウェアハウスにおける転送」を参照してください。他の抽出テクニックに比べてパフォーマンスと管理性が大幅に向上するため、できるかぎりトランスポータブル表領域を使用することをお薦めします。

エクスポート/インポート使用の詳細は、『Oracle Databaseユーティリティ』を参照してください。

チェンジ・データ・キャプチャ

抽出における重要な考慮点として、増分抽出があります。これはチェンジ・データ・キャプチャとも呼ばれます。業務系システムからデータ・ウェアハウスへのデータ抽出を夜間に行う場合、データ・ウェアハウスに必要なのは、前回の抽出以降に変更されたデータ(過去24時間で変更されたデータ)のみです。また、チェンジ・データ・キャプチャは、ほぼリアルタイムまたはオンタイムのデータ・ウェアハウスを提供するキー有効化テクノロジでもあります。

変更された最新データのみを効率的に識別して抽出できれば、データ抽出ボリュームがわずかで済み、抽出プロセス(およびETLプロセスの下流の処理)を大幅に効率化できます。ただし、多くのソース・システムでは最新の変更データの識別は困難であり、また、システムの操作に影響を及ぼすこともあります。チェンジ・データ・キャプチャは、データ抽出における最も困難な技術課題です。

多くの場合、チェンジ・データ・キャプチャは抽出プロセスの一部として望ましいものですが、チェンジ・データ・キャプチャ機能を使用できない場合があります。そのため、この項では独自開発のチェンジ・キャプチャ機能をOracle Databaseソース・システムに実装するいくつかのテクニックについて説明します。

- タイムスタンプ

- パーティション化

- トリガー

これらのテクニックはソース・システムの特性に基づいていますが、ソース・システムに変更が必要なこともあります。したがって、これらのテクニックを実装する前に、ソース・システムのユーザーが慎重にそれぞれを評価する必要があります。

これらのテクニックは、それぞれ前述のデータ抽出テクニックと連携して機能します。たとえば、データがファイルにアンロードされているか、分散問合せによってアクセスされている場合は、タイムスタンプを使用できます。詳細は、第17章「チェンジ・データ・キャプチャ」を参照してください。

タイムスタンプ

業務系システムの中には、表にタイムスタンプ列を持つものもあります。タイムスタンプは、ある行が最後に変更された日付および時間を示します。業務系システムの表にタイムスタンプを含む列があれば、そのタイムスタンプ列を使用することで最新のデータを簡単に識別できます。たとえば、orders表から今日のデータを抽出するには、次の問合せが有効です。

SELECT * FROM orders
WHERE TRUNC(CAST(order_date AS date),'dd') =
TO_DATE(SYSDATE,'dd-mon-yyyy');

業務系ソース・システムにタイムスタンプ情報がない場合、タイムスタンプを含むようにシステムを変更できるとは限りません。このような変更が必要な場合は、まず、業務系システムの表を変更して、新しくタイムスタンプ列を追加します。次に、特定の行を変更する操作が行われるたびにタイムスタンプ列を更新するトリガーを作成します。

パーティション化

ソース・システムの中には、レンジ・パーティション化を使用しているものもあります。これには、ソース表が日付キーによってパーティション化され、新しいデータが簡単に識別できるようになっているものなどがあります。たとえば、orders表から抽出する場合は、orders表が週別にパーティション化されていれば、現在の週のデータを簡単に識別できます。

トリガー

業務系システムにトリガーを作成することで、最新の更新レコードを追跡できます。これらのトリガーをタイムスタンプ列と連携させて使用し、ある行が最後に変更された正確な日付および時間を識別することもできます。そのためには、データ変更を獲得する必要がある各ソース表にトリガーを作成します。ソース表でDML文が実行されるたびに、このトリガーによってタイムスタンプ列が現在の時間に更新されます。このようにして、行が最後に変更された正確な日付および時間が、タイムスタンプ列に反映されます。

同様に内部化されたトリガー・ベースのテクニックが、Oracleのマテリアライズド・ビュー・ログに使用されています。これらのログは、マテリアライズド・ビューが変更データを識別するために使用し、エンド・ユーザーもアクセスできます。ただし、マテリアライズド・ビュー・ログの形式はドキュメント化されないので、時間が経過すると変化する場合があります。

トリガー・ベースのメカニズムを使用する場合は、同期チェンジ・データ・キャプチャを使用してください。チェンジ・データ・キャプチャで変更情報にアクセスするための外部化インタフェースと、各種クライアントにこの情報を配布するためのメンテナンス・フレームワークが提供されるため、トリガー・ベースの変更のキャプチャには同期チェンジ・データ・キャプチャの使用をお薦めします。

マテリアライズド・ビュー・ログはトリガーに依存しますが、このデータ変更システムの作成およびメンテナンスが主にデータベースで管理されるという点が効果的です。

ただし、チェンジ・データ・キャプチャで変更情報にアクセスするための外部化インタフェースと、各種クライアントにこの情報を配布するためのメンテナンス・フレームワークが提供されるため、トリガーベースの変更のキャプチャには同期チェンジ・データ・キャプチャの使用をお薦めします。

トリガー・ベース・テクニックはソース・システムのパフォーマンスに影響する場合があるため、本番ソース・システムに実装する前にその点を慎重に検討する必要があります。

データ・ウェアハウスにおける抽出の例

次の2つのデータ抽出方法があります。

- データファイルを使用した抽出

- 分散処理による抽出

データファイルを使用した抽出

ほとんどのデータベース・システムには、内部データベース・フォーマットからデータをフラット・ファイルにエクスポートまたはアンロードする機能が搭載されています。メインフレーム・システムからの抽出では、COBOLプログラムが使用されることがありますが、データベースの多くは、エクスポートまたはアンロード用のユーティリティを搭載しています。

データの抽出では、必ずしもデータベースの全構造がフラット・ファイルにアンロードされるわけではありません。多くの場合、データベース表全体またはオブジェクト全体をアンロードすることが適切です。前回の抽出以降にソース・システムで行われた変更のような、特定の表のサブセットのみ、または複数の表を結合した結果のみをアンロードする方がより適切な場合もあります。抽出テクニックによって、これらの2つのいずれが適切かが異なります。

ソース・システムがOracleデータベースの場合、データをファイルに抽出するには、次のように何通りかの方法があります。

- SQL*Plusによるフラット・ファイルへの抽出

- OCIまたはPro*Cプログラムによるフラット・ファイルへの抽出

- エクスポート・ユーティリティによるエクスポート・ファイルへの抽出

- 外部表によるエクスポート・ファイルへの抽出

SQL*Plusによるフラット・ファイルへの抽出

データ抽出の最も基本的なテクニックは、SQL*PlusでSQL問合せを実行して、問合せの出力をファイルに送ることです。たとえば、フラット・ファイルcountry_city.logを抽出するには、次のSQLスクリプトを実行します。このファイルでは、列値の間にデリミタとしてパイプ記号が使用されており、表countriesおよびcustomersからのアメリカの市のリストが含まれています。

SET echo off SET pagesize 0 SPOOL country_city.log
SELECT distinct t1.country_name ||'|'|| t2.cust_city
FROM countries t1, customers t2 WHERE t1.country_id = t2.country_id
AND t1.country_name= 'United States of America';
SPOOL off

出力ファイルの抽出フォーマットは、SQL*Plusのシステム変数を使用して指定できます。

この抽出テクニックは、カスタマイズされたフォーマットで結果を格納するのに効果的です。外部表データ・ポンプ・アンロード機能を使用すると、任意のSQL操作の結果を抽出することもできます。前述の例では、結合の結果を抽出します。

この抽出テクニックは、複数の同時SQL*Plusセッションを起動し、各セッションで、抽出対象データの異なる部分を表す別々の問合せを実行することで、パラレル化できます。たとえば、orders表からデータを抽出するとき、そのorders表が月別にレンジ・パーティション化されており、orders_jan1998、orders_feb1998などのパーティションがあるとします。orders表から1年分のデータを抽出するには、12の同時SQL*Plusセッションを起動し、各セッションでパーティションを1つずつ抽出します。このようなセッションを行うSQLスクリプトの例を、次に示します。

SPOOL order_jan.dat
SELECT * FROM orders PARTITION (orders_jan1998);
SPOOL OFF

これらの12のSQL*Plusプロセスによって、データが12の別々のファイルに同時にスプールされます。必要な場合は、抽出後に(オペレーティング・システムのユーティリティを使用して)これらのファイルを連結できます。ターゲットへのロードにSQL*Loaderを使用する場合は、この12のファイルをそのまま使用して12のSQL*Loaderセッションでパラレル・ロードできます。例については、第14章「データ・ウェアハウスにおける転送」を参照してください。

orders表がパーティション化されていない場合でも、論理的または物理的な基準に基づいて抽出をパラレル化できます。論理的な方法は、次のように列値の論理的な範囲に基づいています。

SELECT ... WHERE order_date
BETWEEN TO_DATE('01-JAN-99') AND TO_DATE('31-JAN-99');

物理的な方法は、値の範囲に基づいています。データ・ディクショナリを参照することで、orders表を構成するOracle Databaseデータ・ブロックを識別できます。この情報を使用して、orders表からデータを抽出するためのROWIDレンジ問合せの集合を導出できます。

SELECT * FROM orders WHERE rowid BETWEEN value1 and value2;

複雑なSQL問合せによる抽出もパラレル化できますが、1つの複雑な問合せを複数のコンポーネントに分割することが困難な場合があります。特に、独立したプロセスを調整してグローバルな一貫性を持つビューを保証するのは困難な場合があります。SQL*Plusの方法と異なり、外部表データ・ポンプ・アンロード機能では透過的にパラレル化できます。

パラレル化のすべてのテクニックは、ソース・システムのCPUおよびI/Oリソースを大量に使用します。抽出テクニックをパラレル化する前に、ソース・システムへの影響を評価する必要があることに注意してください。

OCIまたはPro*Cプログラムによるフラット・ファイルへの抽出

OCIプログラム(または、Pro*CプログラムなどOracle Call Interfaceを使用する他のプログラム)も、データ抽出に使用できます。これらのテクニックによって、通常、SQL*Plusを使用する方法より高いパフォーマンスが得られますが、プログラミングには時間がかかります。SQL*Plusの方法と同様に、OCIプログラムもSQL問合せの結果の抽出に使用できます。また、SQL*Plusの方法で説明したパラレル化のテクニックもOCIプログラムに対して簡単に適用できます。

OCIまたはSQL*Plusを抽出に使用する場合は、データ自体のみでなく追加情報が必要になります。少なくとも、抽出される列に関する情報が必要です。また、抽出フォーマットがわかっていれば役立ちます。これには、個別の列間のセパレータなどがあります。

エクスポート・ユーティリティによるエクスポート・ファイルへの抽出

エクスポート・ユーティリティによって、表(データ含む)をOracle Databaseエクスポート・ファイルにエクスポートできます。SQL問合せの結果を抽出するSQL*PlusおよびOCIの方法とは異なり、エクスポート・ユーティリティではデータベースのオブジェクトを抽出するメカニズムが提供されます。そのため、エクスポート・ユーティリティは前述の2つの方法とは大きく異なる点があります。

- エクスポート・ファイルには、データの他にメタデータも含まれます。エクスポート・ファイルには、表の生データのみでなく表を再作成するための情報も含まれ、索引、制約、権限付与、およびその表に関係する他の属性があればそれらも含まれます。

- 1つのエクスポート・ファイルにはシングル・オブジェクトのサブセットまたは多数のデータベース・オブジェクトが含まれ、スキーマ全体が含まれることもあります。

- エクスポート・ユーティリティは、複雑なSQL問合せの結果のエクスポートには直接使用できません。エクスポートは、個々のデータベース・オブジェクトのサブセットの抽出にのみ使用できます。

- エクスポート・ユーティリティの出力は、インポート・ユーティリティを使用して処理する必要があります。

Oracleでは、下位互換性用の元のエクスポート/インポート・ユーティリティ、および高パフォーマンスでスケーラブルなパラレル抽出のためのデータ・ポンプ・エクスポート/インポート・インフラストラクチャを提供しています。詳細は、『Oracle Databaseユーティリティ』を参照してください。

外部表によるエクスポート・ファイルへの抽出

エクスポート・ユーティリティの他に、外部表を使用して任意のSELECT操作の結果を抽出できます。データは、プラットフォームに依存しないOracle内部データ・ポンプ形式で格納され、ターゲット・システムで通常の外部表として処理できます。次の例では、パラレルの結合操作の結果を、指定した4つのファイルに抽出します。データ抽出用に使用できる外部表のタイプは、Oracle内部形式ORACLE_DATAPUMPのみです。

CREATE DIRECTORY def_dir AS '/net/dlsun48/private/hbaer/WORK/FEATURES/et';
DROP TABLE extract_cust;
CREATE TABLE extract_cust
ORGANIZATION EXTERNAL
(TYPE ORACLE_DATAPUMP DEFAULT DIRECTORY def_dir ACCESS PARAMETERS
(NOBADFILE NOLOGFILE)
LOCATION ('extract_cust1.exp', 'extract_cust2.exp', 'extract_cust3.exp',
'extract_cust4.exp'))
PARALLEL 4 REJECT LIMIT UNLIMITED AS
SELECT  c.*, co.country_name, co.country_subregion, co.country_region
FROM customers c, countries co where co.country_id=c.country_id;

指定した抽出ファイルの合計数により、書込み操作の最大並列度が制限されます。抽出をパラレル化しても、文のSELECT部分は自動的にパラレル化されないことに注意してください。

エクスポート/インポートを使用する場合とは異なり、外部表データ・ポンプ・アンロードを使用すると、外部表のメタデータは作成されたファイルには含まれません。外部表の適切なメタデータを抽出するには、次の文に示すとおりDBMS_METADATAパッケージを使用します。

SET LONG 2000
SELECT DBMS_METADATA.GET_DDL('TABLE','EXTRACT_CUST') FROM DUAL;

分散処理による抽出

分散問合せテクノロジを使用すると、1つのOracleデータベースから、他のOracleデータベースやOracleゲートウェイ・テクノロジで接続されている従来型システムなど、各種のソース・システムにある表へ直接問い合せることができます。具体的には、データ・ウェアハウスまたはステージング・データベースから、接続されているソース・システムにある表やデータに直接アクセスできます。ゲートウェイは、分散問合せテクノロジのもう1つの形式です。ゲートウェイでは、Oracleデータベース(データ・ウェアハウスなど)からリモートの非Oracleデータベースに格納されているデータベース表にアクセスできます。2つのOracleデータベース間でデータを移動するには、これが最も簡単な方法です。抽出と変換のプロセスを1ステップで行うことができるうえ、プログラミングに必要な時間も最小限に抑えられます。ただし、これが常に実現可能であるとは限りません。

部署名を含む従業員名リストをソース・データベースから抽出し、それをデータ・ウェアハウスに格納するとします。Oracle Net接続および分散問合せテクノロジを使用すると、この作業は、次の1つのSQL文で実現できます。

CREATE TABLE country_city AS SELECT distinct t1.country_name, t2.cust_city
FROM countries@source_db t1, customers@source_db t2
WHERE t1.country_id = t2.country_id
AND t1.country_name='United States of America';

この文により、データ・マートにローカル表country_cityが作成され、ソース・システム上の表countriesおよびcustomersからデータが移入されます。

このテクニックは少量のデータ移動には理想的です。ただし、データは単一のOracle Net接続を介してソース・システムからデータ・ウェアハウスに転送されます。そのため、このテクニックのスケーラビリティには限界があります。データが大量になると、ファイル・ベースのデータ抽出および転送テクニックの方が、よりスケーラブルで適切であることがあります。

関連項目:

- 分散問合せの詳細は、『Oracle Database Heterogeneous Connectivity管理者ガイド』を参照してください。

- 分散問合せの詳細は、『Oracle Database概要』を参照してください。

14 データ・ウェアハウスにおける転送

この章では、データ・ウェアハウスへのデータの転送について説明します。

- データ・ウェアハウスにおける転送の概要

- データ・ウェアハウスにおける転送メカニズムの概要

データ・ウェアハウスにおける転送の概要

転送とは、データをあるシステムから別のシステムへ移動させる操作です。データ・ウェアハウス環境では、最も一般的な転送要件は、次のデータ移動です。

- ソース・システムからステージング・データベースまたはデータ・ウェアハウス・データベースへ

- ステージング・データベースからデータ・ウェアハウスへ

- データ・ウェアハウスからデータ・マートへ

転送は、ETLプロセスの中でも単純な部分であることが多く、一般的にプロセスの他の部分と統合できます。たとえば、第13章「データ・ウェアハウスにおける抽出」に示すように、分散問合せテクノロジにはデータ抽出と転送の両方のメカニズムが備わっています。

データ・ウェアハウスにおける転送メカニズムの概要

ウェアハウスにおけるデータ転送には、次の3つの基本的な方法があります。

- フラット・ファイルを使用した転送

- 分散処理による転送

- トランスポータブル表領域を使用した転送

フラット・ファイルを使用した転送

データ転送の最も一般的な方法は、FTPや他のリモート・ファイル・システム・アクセス・プロトコルなどのメカニズムを使用して、フラット・ファイルを転送する方法です。データは、第13章「データ・ウェアハウスにおける抽出」で説明したテクニックで、ソース・システムからフラット・ファイルにアンロードまたはエクスポートされ、FTPまたは同等のメカニズムによってターゲット・プラットフォームに転送されます。

ソース・システムとデータ・ウェアハウスでは、異なるオペレーティング・システムとデータベース・システムを使用していることが多いため、データ変換を最小限に抑えて異機種システム間でデータを交換するには、フラット・ファイルを使用するのが最も単純な方法です。ただし、同機種システム間でのデータ転送でも、フラット・ファイルは最も効率的で扱いやすいデータ転送メカニズムです。

分散処理による転送

分散問合せは、ゲートウェイを使用するかどうかを問わずデータ抽出の効果的なメカニズムです。これらのメカニズムでは、ターゲット・システムに直接データを転送するため、抽出と変換を1ステップで処理します。時間とシステム・リソースに許される影響度によっては、これらのメカニズムは抽出と変換の両方に適しています。

フラット・ファイルの転送とは異なり、転送の成否は分散問合せまたはトランザクションの結果とともに即時に認識されます。詳細は、第13章「データ・ウェアハウスにおける抽出」を参照してください。

トランスポータブル表領域を使用した転送

Oracleトランスポータブル表領域により、大量のデータを2つのOracleデータベース間で最も高速に移動させることができます。トランスポータブル表領域の導入以前は、最もスケーラブルなデータ転送メカニズムは、生データを含むフラット・ファイルの移動によるものでした。このようなメカニズムでは、データをまずソース・データベースからアンロードまたはエクスポートし、転送後に、ターゲット・データベースにロードまたはインポートする必要がありました。トランスポータブル表領域ではこのアンロードと再ロードのステップは不要です。

トランスポータブル表領域を使用することによって、Oracleデータファイル(表データ、索引およびその他のOracleデータベース・オブジェクトのほぼすべてを含む)を、あるデータベースから他のデータベースへ直接転送できます。さらに、トランスポータブル表領域は、インポートおよびエクスポートと同様に、データのみでなくメタデータを転送するメカニズムも提供します。

トランスポータブル表領域には、いくつかの制限があり、ソース・システムおよびターゲット・システムでは、Oracle8i(またはそれ以上)が稼働していること、互換性のあるキャラクタ・セットを使用すること、Oracle Database 10gより前では同一のオペレーティング・システムで稼働することが必要です。オペレーティング・システム間で表領域を転送する方法の詳細は、『Oracle Database管理者ガイド』を参照してください。

データ・ウェアハウスにおけるトランスポータブル表領域の最も一般的な用途は、ステージング・データベースからデータ・ウェアハウスへのデータ移動、またはデータ・ウェアハウスからデータ・マートへのデータ移動です。

トランスポータブル表領域の例

売上データを含む1つのデータ・ウェアハウス、および毎月リフレッシュされる複数のデータ・マートがあるとします。また、1か月分の売上データをデータ・ウェアハウスからデータ・マートに移動するとします。

手順1   転送するデータを専用の表領域に配置する

現在の月のデータを転送するには、まず、別々に用意した表領域にそのデータを配置する必要があります。この例では、ts_temp_sales表領域に今月のデータをコピーするとします。CREATE TABLE ... AS SELECT文を使用すると、当月のデータを効率的にこの表領域にコピーできます。

CREATE TABLE temp_jan_sales NOLOGGING TABLESPACE ts_temp_sales
AS SELECT * FROM sales
WHERE time_id BETWEEN '31-DEC-1999' AND '01-FEB-2000';

この操作の完了後に、表領域ts_temp_salesを読取り専用に設定します。

ALTER TABLESPACE ts_temp_sales READ ONLY;

表領域は、その表領域を変更するアクティブなトランザクションがなくなるまで転送できません。表領域を読取り専用に設定することによって、転送が可能になります。

ts_temp_sales表領域は、トランスポータブル表領域機能が使用する一時的なデータ格納領域として、特別に作成したものの場合もあります。「データファイルおよびエクスポート・ファイルをターゲット・システムにコピーする」の後、この表領域を読取り/書込みに設定できます。必要に応じて、temp_jan_sales表を削除したり、その表領域を他のデータ転送やそれ以外の目的で再使用できます。

トランスポータブル表領域操作では、任意の表領域にあるすべてのオブジェクトが転送されます。この例では1つの表のみが転送されていますが、ts_temp_sales表領域には複数の表が含まれることもあります。たとえば、データ・マートのリフレッシュは、最新の月の売上トランザクションによってのみでなく、CUSTOMER表の新規コピーによって行われることもあります。これらの2つの表は、どちらも同じ表領域に転送できます。また、この表領域には、索引など他のデータベース・オブジェクトも含めることができ、それらも同様に転送されます。

また、トランスポータブル表領域操作では、複数の表領域を同時に転送できます。これによって、非常に大量のデータも簡単にデータベース間で移動できます。ただし、トランスポータブル表領域機能で転送できるのは、他の表領域に依存性を持たないデータベース・オブジェクトの完全な集合を含む表領域の集合のみであることに注意してください。たとえば、索引は、元となる表がなければ転送できません。また、パーティションも表の残りの部分がないと転送できません。DBMS_TTSパッケージを使用すると、表領域がトランスポータブルであるかどうかをチェックできます。

参照:
DBMS_TTSパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

手順1では、1月の売上データを別々の表領域にコピーしました。ただし、別々の表領域にデータを移動しなくても、トランスポータブル表領域を利用できる場合もあります。SALES表がデータ・ウェアハウス内で月別にパーティション化されており、各パーティションが専用の表領域にある場合、1月のデータを含む表領域を直接転送できることがあります。たとえば、表領域ts_sales_jan2000に、1月のパーティションsales_jan2000が配置されているとします。この場合、1月の売上データを一時的にts_temp_salesにコピーするのではなく、ts_sales_jan2000表領域を転送できることがあります。

ただし、表領域ts_sales_jan2000を転送するには、特別に作成された表領域の場合と同じ条件を満たす必要があります。第1に、この表領域をREAD ONLYに設定する必要があります。第2に、パーティション表のパーティション1つのみを転送することはできない(パーティション表の残りの部分も転送する必要がある)ため、1月のデータのみを転送するには、(ALTER TABLE文を使用して)1月のパーティションを別の表に変換する必要があります。EXCHANGE操作は非常に高速です。ただし、1月のデータは基礎となるsales表の一部ではなくなるため、メタデータのエクスポート後に変換してsales表に戻さないかぎり、ユーザーからはアクセスできなくなります。1月のデータは、手順3の完了後に変換してsales表に戻すことができます。

手順2   メタデータをエクスポートする

転送した表領域に含まれるオブジェクトを記述するメタデータをエクスポートするには、エクスポート・ユーティリティを使用します。これまでの例に合せてエクスポート・コマンドを記述すると、次のようになります。

EXP TRANSPORT_TABLESPACE=y TABLESPACES=ts_temp_sales FILE=jan_sales.dmp

この操作では、エクスポート・ファイルjan_sales.dmpが生成されます。このエクスポート・ファイルは、含まれているのがメタデータのみのためサイズは小さくなっています。この場合、エクスポート・ファイルには、列名、列のデータ型などの表temp_jan_salesを説明する情報、およびターゲットOracleデータベースがts_temp_sales内のオブジェクトへのアクセスに必要とするその他の情報すべてが含まれます。

手順3   データファイルおよびエクスポート・ファイルをターゲット・システムにコピーする

ts_temp_salesを構成するデータファイル、およびエクスポート・ファイルjan_sales.dmpを、いずれかのフラット・ファイル転送メカニズムを使用してデータ・マート・プラットフォームにコピーします。データファイルのコピーが終わると、必要に応じて表領域ts_temp_salesをREAD WRITEモードに設定できます。

手順4   メタデータをインポートする

ファイルをデータ・マートにコピーした後に、メタデータをデータ・マートにインポートします。

IMP TRANSPORT_TABLESPACE=y DATAFILES='/db/tempjan.f'
TABLESPACES=ts_temp_sales FILE=jan_sales.dmp

この時点で、表領域ts_temp_salesおよび表temp_sales_janが、データ・マート内でアクセス可能になります。この新しいデータをデータ・マートの表に取込むことができます。

temp_sales_jan表からデータ・マートのsales表にデータを挿入するには、2通りの方法があります。

INSERT /*+ APPEND */ INTO sales SELECT * FROM temp_sales_jan;

この操作の後に、temp_sales_jan表(およびts_temp_sales表領域全体)を削除できます。

または、データ・マートのsales表が月別にパーティション化されている場合に、新しく転送した表領域およびtemp_sales_jan表をそのデータ・マートの永続部分にすることが可能です。temp_sales_jan表は、データ・マートのsales表のパーティションになることができます。

ALTER TABLE sales ADD PARTITION sales_00jan VALUES
LESS THAN (TO_DATE('01-feb-2000','dd-mon-yyyy'));
ALTER TABLE sales EXCHANGE PARTITION sales_00jan
WITH TABLE temp_sales_jan INCLUDING INDEXES WITH VALIDATION;

トランスポータブル表領域の他の用途

前述の例では、データ・ウェアハウスにデータを転送する代表的な例を示しました。トランスポータブル表領域は、この他にも様々な目的に使用できます。データ・ウェアハウス環境では、トランスポータブル表領域は、Oracleデータベース間で大量のデータを移動する(インポート/エクスポート、またはSQL*Loaderのような)ユーティリティとみなすことができます。CREATE TABLE ... AS SELECT文やINSERT ... AS SELECT文などのパラレル・データ移動操作とともに使用すると、トランスポータブル表領域は、様々な目的でのデータの高速転送用の重要なメカニズムを提供します。

15 ロードおよび変換

この章は、データ・ウェアハウスの作成および管理に有効な情報について説明します。内容は次のとおりです。

- データ・ウェアハウスにおけるロードおよび変換の概要

- ロード・メカニズム

- 変換メカニズム

- エラーのロギングおよび処理のメカニズム

- ロードおよび変換の使用例

データ・ウェアハウスにおけるロードおよび変換の概要

データ変換は最も複雑で、抽出、変換およびロード(ETL)プロセスの中で最も処理時間がかかることがあります。データ変換では、単純なデータ変換からかなり複雑なデータのクレンジング・テクニックまで実行できます。変換は、データベース外(フラット・ファイルなど)でも実装されることがありますが、ほとんどの場合はOracleデータベース内で行います。

この章では、Oracle Database内でスケーラブルで効果的なデータ変換を実装するテクニックについて説明します。この章の例は比較的単純です。実際のデータ変換は、通常、はるかに複雑です。ただし、この章で説明した変換テクニックは、実際のデータ変換要件のほとんどを満たしており、その他の方法よりスケーラブルで、少ないプログラミングで済みます。

この章では、データ・ウェアハウスで発生する一般的な変換をすべて説明しているわけではありません。これらの変換を実装するために使用できる基本的なテクニックの種類を示し、最適なテクニックの選択方法を説明します。

変換フロー

アーキテクチャの観点では、データ変換には次の2通りの方法があります。

- マルチステージ・データ変換

- パイプライン・データ変換

- ステージング領域

マルチステージ・データ変換

ほとんどのデータ・ウェアハウスのデータ変換のロジックは、複数のステップから構成されています。たとえば、sales表に挿入するために新しいレコードを変換する場合、各ディメンション・キーの妥当性チェックを行うには、個別のロジック変換ステップに従う必要がある場合があります。

図15-1は、変換ロジックをグラフィカルに示したものです。

図15-1 マルチステージ・データ変換

Oracle Databaseを変換エンジンとして使用する場合、一般的な方法では、変換をそれぞれ別々のSQL操作として実装し、各ステップの処理結果を格納するために別々の一時的なステージング表(図15-1の表new_sales_step1やnew_sales_step2など)を作成します。また、ロードしてから変換するこの方法では、変換プロセス全体にチェックポイント取得を実行するスキームが提供されます。このスキームによって、プロセスの監視および再起動が簡単になります。ただし、マルチステージングには、領域と時間の必要性が増大するというデメリットもあります。

また、多数の単純なロジック変換を、単一のSQL文または単一のPL/SQLプロシージャに結合することも可能です。このような結合を行うと、各ステップを個別に実行するよりパフォーマンスが向上することがありますが、同時に、個々の変換の変更、追加、削除や、失敗した変換からのリカバリが困難になる場合があります。

パイプライン・データ変換

ETLプロセス・フローは動的に変更でき、データベースはETLソリューションに不可欠の部品になります。

新機能により従来は必要だったプロセス・ステップの一部が廃止になりますが、改造してデータ・フローとデータ変換を強化し、よりスケーラブルで中断のないものにできます。タスクは変換してからロードするという(ほとんどのタスクがデータベース外部で実行される)シリアル・プロセスや、ロードしてから変換するというプロセスから拡張され、ロードしながら変換するプロセスへとシフトします。

Oracleでは、ETLシナリオに関連するすべての問題とタスクに対処できるように、多様な新機能が用意されています。データベースは、汎用的なソリューションに対処するのではなく、ツールキット機能を提供するということを理解する必要があります。基礎となるデータベースは、特定の顧客のニーズに合せて最も適切なETLプロセス・フローを有効化する必要があり、技術的な観点での要件や制約がないようにする必要があります。図15-2に、以降で説明する新機能を示します。

図15-2 パイプライン・データ変換

ステージング領域

ロードの全体的な速度は、生データをいかに速くステージング領域から読み取ることができるか、データベースのターゲット表に書き込むことができるかにより決まります。生データはできるだけ多くの物理ディスクにステージングし、生データの読取りがロード時のボトルネックにならないようにすることを強くお薦めします。

データをステージングするのに最適な場所はOracle Database File System(DBFS)です。DBFSはSecureFile LOBとしてデータベースに格納されたファイルへのアクセスに使用できるマウント可能なファイルシステムを作成します。DBFSはローカルのファイルシステムのように見える共有ネットワーク・ファイルシステムを提供するという点でNFSと似ています。DBFSはデータ・ウェアハウスとは別のデータベースに作成し、DIRECT_IOオプションを使用してマウントして、ファイルシステムとの間の生データ・ファイルの移動時にシステム・ページ・キャッシュの競合が起きないようにします。DBFSの設定の詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。

ロード・メカニズム

データ・ウェアハウスへのロードには、次のメカニズムを使用できます。

- SQL*Loaderを使用したデータ・ウェアハウスのロード

- 外部表を使用したデータ・ウェアハウスのロード

- OCIおよびダイレクト・パスAPIを使用したデータ・ウェアハウスのロード

- エクスポート/インポートを使用したデータ・ウェアハウスのロード

SQL*Loaderを使用したデータ・ウェアハウスのロード

データ変換をデータベース内で行うには、生データがデータベースでアクセス可能になっている必要があります。その方法の1つは、データベースにロードすることです。データをOracleデータ・ウェアハウスに転送するいくつかのテクニックについては、第14章「データ・ウェアハウスにおける転送」を参照してください。データを転送する最も一般的なテクニックは、フラット・ファイルを使用する方法です。

データをフラット・ファイルからOracleデータ・ウェアハウスへ移動するには、SQL*Loaderを使用します。このデータのロード中に、SQL*Loaderを使用して、基本的なデータ変換の実装を行うこともできます。ダイレクト・パスSQL*Loaderを使用すると、データ型変換や単純なNULL処理などの基本的なデータ操作は、データのロード中に自動的に解決できます。パフォーマンスの理由から、ほとんどのデータ・ウェアハウスでは、ダイレクト・パス・ロードが選択されます。

従来型パス・ローダーは、ダイレクト・パス・ローダーより広範囲な機能をデータ変換で使用できます。SQL関数は、列の値がロードされるときに、すべての列に適用できます。これによって、データのロード中に、豊富な機能を使用して変換できるようになります。ただし、従来型パス・ローダーはダイレクト・パス・ローダーより低速です。このような理由から、従来型パス・ローダーを使用するのは、主に少量のデータをロードおよび変換する場合にしてください。

ここでは、SQL*Loader制御ファイルの単純な例を示します。このファイルでは、外部ファイルsh_sales.datからshサンプル・スキーマのsales表にデータがロードされます。外部フラット・ファイルsh_sales.datは、日次レベルで集計された売上トランザクション・データで構成されています。この外部ファイルのすべての列がsales表にロードされるわけではありません。この外部ファイルは、shサンプル・スキーマの2番目のファクト表をロードするためのソースとしても使用されます。これには、外部表が使用されます。

次に、sales表をロードする制御ファイル(sh_sales.ctl)を示します。

LOAD DATA INFILE sh_sales.dat APPEND INTO TABLE sales
FIELDS TERMINATED BY "|"
(PROD_ID, CUST_ID, TIME_ID, CHANNEL_ID, PROMO_ID, QUANTITY_SOLD, AMOUNT_SOLD)

このロードには、次のコマンドを使用できます。

$  sqlldr control=sh_sales.ctl direct=true
Username:
Password:

外部表を使用したデータ・ウェアハウスのロード

外部データソースを処理するもう1つのアプローチは、外部表を使用することです。Oracleの外部表機能では、外部データを直接およびパラレルに問い合せて結合できる仮想表として使用できます。この場合、外部データを最初にデータベースにロードする必要はありません。そのまま、SQL、PL/SQLおよびJavaを使用して外部データにアクセスできます。

外部表を使用すると、ロード・フェーズを変換フェーズとパイプライン化できます。データ・ストリーミングを中断せずに、変換プロセスをロード・プロセスとマージできます。データベースで比較や変換などの処理を行うために、データベース内でデータをステージングする必要はなくなります。たとえば、外部表からのSELECTと組み合わせたダイレクト・パスのINSERT AS SELECT文で、従来型ロードの変換機能を使用できます。

外部表と通常の表の主な違いは、外部で編成された表は読取り専用であることです。DML操作(UPDATE/INSERT/DELETE)は実行できず、索引も作成できません。

外部表は既存のSQL*Loader機能の大部分に準拠しており、ほとんどの場合において、より優れた機能を提供します。外部表は、外部ソース全体を既存のデータベース・オブジェクトと結合する必要がある環境、またはデータを複雑な方法で変換する必要がある環境で特に有効です。たとえば、SQL*Loaderとは異なり、任意のSQL変換を適用してダイレクト・パス・インサートを使用できます。さらに、ファイル(圧縮されたデータファイルなど)を処理する実行対象のプログラム(zcatなど)を指定し、その出力(圧縮されていないデータファイルなど)をOracle Databaseで利用できるようにすることもできます。つまり、大量の圧縮データをロードする際に、先にそのデータをディスクに展開する必要がないということです。

外部ファイルsh_sales.gzに表される、売上トランザクション・データ全体の構造を表す外部表sales_transactions_extを作成できます。製品部門では、製品と時間単位の原価分析が特に重要です。したがって、shスキーマにファクト表costを作成します。処理するソース・データは、salesファクト表の場合と同じです。ただし、提供される全ディメンション情報を調査するわけではないため、costファクト表のデータはsalesファクト表のデータより疎い密度になっています。たとえば、すべての異なる物流チャネルが1つに集計されます。

ディメンションの一部は使用されないため、前述のような詳細情報の集計を行わないとcostファクト表にデータをロードできません。

外部表フレームワークは、そのソリューションを提供します。SQL*Loaderでは集計を適用する前にデータをロードする必要がありましたが、それとは異なり、次のように単一のSQL DML文中にロードと変換を組み合せることができます。ターゲット表に挿入する前にデータを一時的にステージングする必要はありません。

オブジェクト・ディレクトリがすでに存在し、sh_sales.gzファイルを含むディレクトリと不良ファイルおよびログ・ファイルを含むディレクトリを指定する必要があります。

CREATE TABLE sales_transactions_ext
(PROD_ID NUMBER, CUST_ID NUMBER,
TIME_ID DATE, CHANNEL_ID NUMBER,
PROMO_ID NUMBER, QUANTITY_SOLD NUMBER,
AMOUNT_SOLD NUMBER(10,2), UNIT_COST NUMBER(10,2),
UNIT_PRICE NUMBER(10,2))
ORGANIZATION external (TYPE oracle_loader
DEFAULT DIRECTORY data_file_dir ACCESS PARAMETERS
(RECORDS DELIMITED BY NEWLINE CHARACTERSET US7ASCII
PREPROCESSOR EXECDIR:'zcat'
BADFILE log_file_dir:'sh_sales.bad_xt'
LOGFILE log_file_dir:'sh_sales.log_xt'
FIELDS TERMINATED BY "|" LDRTRIM
( PROD_ID, CUST_ID,
TIME_ID         DATE(10) "YYYY-MM-DD",
CHANNEL_ID, PROMO_ID, QUANTITY_SOLD, AMOUNT_SOLD,
UNIT_COST, UNIT_PRICE))
location ('sh_sales.gz')
)REJECT LIMIT UNLIMITED;

これで、外部表をデータベースから使用し、外部データの一部の列にのみアクセスし、データをグルーピングして、costsファクト表に挿入できます。

INSERT /*+ APPEND */ INTO COSTS
(TIME_ID, PROD_ID, UNIT_COST, UNIT_PRICE)
SELECT TIME_ID, PROD_ID, AVG(UNIT_COST), AVG(amount_sold/quantity_sold)
FROM sales_transactions_ext GROUP BY time_id, prod_id;

参照:

- 外部表の構文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

- 使用例は『Oracle Databaseユーティリティ』を参照してください。

OCIおよびダイレクト・パスAPIを使用したデータ・ウェアハウスのロード

OCIおよびダイレクト・パスAPIが頻繁に使用されるのは、変換と計算がデータベースの外部で実行され、フラット・ファイルのステージングを必要としない場合です。

エクスポート/インポートを使用したデータ・ウェアハウスのロード

エクスポートとインポートは、データがそのままターゲット・システムに挿入される場合に使用されます。複雑な抽出は実行できません。詳細は、第13章「データ・ウェアハウスにおける抽出」を参照してください。

変換メカニズム

データベース内でデータを変換するには、次の方法があります。

- SQLを使用したデータの変換

- PL/SQLを使用したデータの変換

- テーブル・ファンクションを使用したデータの変換

SQLを使用したデータの変換

データがデータベースにロードされると、SQL操作を使用してデータ変換を実行できます。SQLデータ変換を実装するには、次の4つの基本的なテクニックがあります。

- CREATE TABLE ... AS SELECTおよびINSERT /*+APPEND*/ AS SELECT

- UPDATEを使用したデータの変換

- MERGEを使用したデータの変換

- マルチテーブル・インサートを使用したデータの変換

CREATE TABLE ... AS SELECTおよびINSERT /*+APPEND*/ AS SELECT

CREATE TABLE ... AS SELECT文(CTAS)は、大規模なデータセットを操作する場合に強力なツールです。後述する例のように、多くのデータ変換は標準SQLで記述でき、CTASにより、SQL問合せを効果的に実行してその問合せ結果を新しいデータベース表に格納する機能が提供されます。INSERT /*+APPEND*/ ...AS SELECT文の機能は、既存のデータベース表に対して同様の機能を提供します。

データ・ウェアハウス環境では、CTASは、最大のパフォーマンスを得るためにNOLOGGINGモードで、パラレルで実行されます。

簡単で一般的なデータ変換は、データの置換えです。データ置換えによる変換では、1つの列のいくつかまたはすべての値が変更されます。たとえば、sales表にchannel_id列があるとします。この列は、指定された売上トランザクションが企業自体の売上(直接販売)によるものか、または販売店(間接販売)によるものかを指定するために使用されます。

データ・ウェアハウスの複数のソース・システムからデータを受け取る場合があるとします。これらのソース・システムのうち、直接販売のみを処理し、間接販売チャネルには関与していないソース・システムがあるとします。データ・ウェアハウスがこのシステムから売上データを最初に受け取ると、すべての売上レコードのsales.channel_idフィールドはNULL値になります。これらのNULL値は、適切なキー値に設定する必要があります。たとえば、ターゲットとなるsales表の文への挿入の一部としてSQL関数を使用すると、この操作を効率的に行うことができます。ソース表sales_activity_directの構造は、次のとおりです。

DESC sales_activity_direct
Name           Null?    Type
------------   -----    ----------------
SALES_DATE              DATE
PRODUCT_ID              NUMBER
CUSTOMER_ID             NUMBER
PROMOTION_ID            NUMBER
AMOUNT                  NUMBER
QUANTITY                NUMBER

次のSQL文は、sales_activity_directのデータをサンプル・スキーマのsales表に挿入します。ここでは、売上日時(sales_date)の値を午前0時に切り捨てるSQL関数を使用し、固定チャネルIDとして3を割り当てます。

INSERT /*+ APPEND NOLOGGING PARALLEL */
INTO sales SELECT product_id, customer_id, TRUNC(sales_date), 3,
promotion_id, quantity, amount
FROM sales_activity_direct;

UPDATEを使用したデータの変換

データ置換を実装するもう1つのテクニックは、UPDATE文を使用してsales.channel_id列を変更することです。UPDATEによって正しい結果が戻されます。ただし、データ置換による変換で多数(またはすべての行)の変更が必要であれば、UPDATEではなくCTAS文を使用する方が効率的な場合があります。

MERGEを使用したデータの変換

Oracle Databaseのマージ機能では、表または表外の単一表ビューに行を条件付きで更新または挿入できるように、SQLキーワードMERGEを導入することでSQLが拡張されます。条件は、ON句に指定します。これは、純粋な大量ロードと並んでデータ・ウェアハウスの同期化における最も一般的な操作です。

マージの例

ここでは、マージの各種実装について説明します。この例では、ディメンション表productsの新規データをデータ・ウェアハウスに転送し、挿入または更新する必要があるとします。表products_deltaの構造はproductsと同じです。

例15-1 SQLを使用したマージ操作

MERGE INTO products t USING products_delta s
ON (t.prod_id=s.prod_id)
WHEN MATCHED THEN UPDATE SET
t.prod_list_price=s.prod_list_price, t.prod_min_price=s.prod_min_price
WHEN NOT MATCHED THEN INSERT (prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc, prod_status,
prod_list_price, prod_min_price)
VALUES (s.prod_id, s.prod_name, s.prod_desc, s.prod_subcategory,
s.prod_subcategory_desc, s.prod_category, s.prod_category_desc,
s.prod_status, s.prod_list_price, s.prod_min_price);

マルチテーブル・インサートを使用したデータの変換

多くの場合、外部データソースは、様々なターゲット・オブジェクトに挿入できるように、論理属性に基づいて分離する必要があります。また、通常、データ・ウェアハウス環境では、同じソース・データが複数のターゲット・オブジェクトに分岐しています。マルチテーブル・インサートでは、この種の変換用に新しいSQL文が用意されており、ビジネス上の変換ルールに応じてデータを複数または1つのターゲットにできます。この挿入は、ビジネス・ルールに基づいて条件付きで行う方法と、無条件で行う方法があります。

これには、複数の表がターゲットとなっている場合に、INSERT ... SELECT文を使用できるというメリットがあります。これにより、2つの別の方法でのデメリットを回避できます。従来は、n個の独立したINSERT … SELECT文を取り扱う必要があり、同じソース・データをn回処理するため、変換による作業負荷もn倍になっていました。または、プロシージャによるアプローチを選択し、挿入の処理方法を行ごとに判断する必要がありました。このソリューションには、SQLで使用可能な高速のアクセス・パスへのダイレクト・アクセスが欠けています。

既存の文を使用する場合と同様に、新しいINSERT ... SELECT文も、パラレル化してダイレクト・ロード機能とともに使用することでパフォーマンスを改善できます。

例15-2 無条件の挿入

次の文では、sales_activity_directに格納されているトランザクションの売上情報が日次で集計され、今日のsalesおよびcostsファクト表に挿入されます。

INSERT ALL
INTO sales VALUES (product_id, customer_id, today, 3, promotion_id,
quantity_per_day, amount_per_day)
INTO costs VALUES (product_id, today, promotion_id, 3,
product_cost, product_price)
SELECT TRUNC(s.sales_date) AS today, s.product_id, s.customer_id,
s.promotion_id, SUM(s.amount) AS amount_per_day, SUM(s.quantity)
quantity_per_day, p.prod_min_price*0.8 AS product_cost, p.prod_list_price
AS product_price
FROM sales_activity_direct s, products p
WHERE s.product_id = p.prod_id AND TRUNC(sales_date) = TRUNC(SYSDATE)
GROUP BY TRUNC(sales_date), s.product_id, s.customer_id, s.promotion_id,
p.prod_min_price*0.8, p.prod_list_price;

例15-3 条件付きのALL挿入

次の文では、有効な宣伝を伴うすべての売上トランザクションについて、salesおよびcosts表に1行が挿入され、ある顧客による複数の同一注文に関する情報が別の表cum_sales_activityに格納されます。売上トランザクションには、2行を挿入できるものと、まったく挿入できないものがあります。

INSERT ALL
WHEN promotion_id IN (SELECT promo_id FROM promotions) THEN
INTO sales VALUES (product_id, customer_id, today, 3, promotion_id,
quantity_per_day, amount_per_day)
INTO costs VALUES (product_id, today, promotion_id, 3,
product_cost, product_price)
WHEN num_of_orders > 1 THEN
INTO cum_sales_activity VALUES (today, product_id, customer_id,
promotion_id, quantity_per_day, amount_per_day, num_of_orders)
SELECT TRUNC(s.sales_date) AS today, s.product_id, s.customer_id,
s.promotion_id, SUM(s.amount) AS amount_per_day, SUM(s.quantity)
quantity_per_day, COUNT(*) num_of_orders, p.prod_min_price*0.8
AS product_cost, p.prod_list_price AS product_price
FROM sales_activity_direct s, products p
WHERE s.product_id = p.prod_id
AND TRUNC(sales_date) = TRUNC(SYSDATE)
GROUP BY TRUNC(sales_date), s.product_id, s.customer_id,
s.promotion_id, p.prod_min_price*0.8, p.prod_list_price;

例15-4 条件付きのFIRST挿入

次の文では、製品注文の合計数量と重量に従って、適切な出荷リストに挿入されます。例外は大量注文の場合で、重量区分が大きすぎないかぎり急送されます。数量のない注文として表されているこの単純な例では、すべての不適切な注文は別の表に格納されます。ここでは、適切な表large_freight_shipping、express_shipping、default_shippingおよびincorrect_sales_orderが存在するものとします。

INSERT FIRST WHEN (sum_quantity_sold > 10 AND prod_weight_class < 5) AND
sum_quantity_sold >=1) OR (sum_quantity_sold > 5 AND prod_weight_class > 5) THEN
INTO large_freight_shipping VALUES
(time_id, cust_id, prod_id, prod_weight_class, sum_quantity_sold)
WHEN sum_amount_sold > 1000 AND sum_quantity_sold >=1 THEN
INTO express_shipping VALUES
(time_id, cust_id, prod_id, prod_weight_class,
sum_amount_sold, sum_quantity_sold)
WHEN (sum_quantity_sold >=1) THEN INTO default_shipping VALUES
(time_id, cust_id, prod_id, sum_quantity_sold)
ELSE INTO incorrect_sales_order VALUES (time_id, cust_id, prod_id)
SELECT s.time_id, s.cust_id, s.prod_id, p.prod_weight_class,
SUM(amount_sold) AS sum_amount_sold,
SUM(quantity_sold) AS sum_quantity_sold
FROM sales s, products p
WHERE s.prod_id = p.prod_id AND s.time_id = TRUNC(SYSDATE)
GROUP BY s.time_id, s.cust_id, s.prod_id, p.prod_weight_class;

例15-5 条件付き挿入と無条件の挿入の混在型

次の例では、新規顧客がcustomers表に挿入され、cust_credit_limitが4501以上のすべての新規顧客がさらなる販促対象として別個の表に格納されます。

INSERT FIRST WHEN cust_credit_limit >= 4500 THEN INTO customers
INTO customers_special VALUES (cust_id, cust_credit_limit)
ELSE INTO customers
SELECT * FROM customers_new;

関連項目:

- MERGE操作の詳細は、第16章「データ・ウェアハウスのメンテナンス」を参照してください。

PL/SQLを使用したデータの変換

データ・ウェアハウス環境では、PL/SQLなどの手続き型言語を使用して、複雑な変換をOracle Databaseで実装できます。CTASが表全体を操作し、パラレル化を強調するのに対して、PL/SQLでは、行ベースの方法で、非常に高度な変換規則に対応できます。たとえば、PL/SQLプロシージャを使用して、複数のカーソルをオープンして複数のソース表からデータを読み取り、複雑なビジネス・ルールを使用してこのデータを結合できます。これによって、変換されたデータを1つ以上のターゲット表に挿入できます。標準SQL文を使用して、同じ手順の操作を表現するのは困難または不可能です。

手続き型言語を使用すると、複雑なETL処理内で特定の変換(または多数の変換ステップ)をカプセル化し、中間のステージング・エリアからデータを読み取り、出力として新しい表オブジェクトを生成できます。以前に生成された変換の入力表と後続の変換には、この特定の変換により生成される表が使用されます。また、ETLプロセス全体でこのようにカプセル化された変換ステップをシームレスに統合できるため、相互の行セットがストリーム化され、中間的なステージングが不要になります。テーブル・ファンクションを使用すると、このような動作を実装できます。

テーブル・ファンクションを使用したデータの変換

テーブル・ファンクションにより、PL/SQL、CまたはJavaで実装された変換のパイプライン実行またはパラレル実行がサポートされます。前述のシナリオは、中間的なステージング表を使用せずに実行でき、各種変換ステップでのデータ・フローは中断されません。

テーブル・ファンクション

テーブル・ファンクションは、出力として行セットを生成できる関数として定義されます。また、テーブル・ファンクションは入力として行セットを使用できます。Oracle9iまでのPL/SQLファンクションには、次のようなデメリットがありました。

- 入力としてカーソルを使用できません。

- パラレル化またはパイプライン化できません。

現在、ファンクションにこのような制限はありません。テーブル・ファンクションでは、次のことができるため、データベース機能が拡張されます。

- 1つの関数から複数行を戻すことができます。

- SQL副問合せ(複数行の選択)の結果を関数に直接渡すことができます。

- 関数に入力としてカーソルを使用できます。

- 関数をパラレル化できます。

- 結果セットが作成されるとすぐに次の処理に段階的に渡します。これは段階的パイプライン処理と呼ばれます。

テーブル・ファンクションは、ネイティブなPL/SQLインタフェースを使用してPL/SQLで定義するか、Oracle Data Cartridge Interface(ODCI)を使用してJavaまたはCで定義できます。

関連項目:

- 『Oracle Database PL/SQL言語リファレンス』

- 『Oracle Databaseデータ・カートリッジ開発者ガイド』

行セットを入力してSUM操作の実行後に行セットを出力する典型的な集計を、図15-3に示します。

図15-3 テーブル・ファンクションの例

この操作のための疑似コードは次のようになります。

INSERT INTO Out SELECT * FROM ("Table Function"(SELECT * FROM In));

このテーブル・ファンクションは、InでのSELECTの結果を入力として使用し、レコード・セットを異なるフォーマットでダイレクト挿入用の出力としてOutに渡します。

また、テーブル・ファンクションでは、データをアトミック・トランザクションの適用範囲内で分岐させることができます。これは、効率的なロギング・メカニズムや、他の独立した変換の分岐など、様々な場合に使用できます。このような使用例では、単一のステージング表が必要です。

図15-4 分岐を伴うパイプライン・パラレル変換

このための疑似コードは次のようになります。

INSERT INTO target SELECT * FROM (tf2(SELECT *
FROM (tf1(SELECT * FROM source))));

この場合は、targetに挿入され、アトミック・トランザクションの適用範囲内でtf1の一部としてStage Table 1にも挿入されます。

INSERT INTO target SELECT * FROM tf3(SELT * FROM stage_table1);

例15-6 テーブル・ファンクションの基本

次の例は、テーブル・ファンクションの基本を示しており、これらの関数内に実装された複雑なビジネス・ルールは使用されていません。あくまでも具体例を示すことが目的であり、すべてPL/SQLで実装されています。

テーブル・ファンクションはレコード・セットを戻し、入力としてカーソルを使用できます。この例を使用する前に、shサンプル・スキーマの他に次のデータベース・オブジェクトを設定する必要があります。

CREATE TYPE product_t AS OBJECT (
prod_id                  NUMBER(6)
, prod_name                VARCHAR2(50)
, prod_desc                VARCHAR2(4000)
, prod_subcategory         VARCHAR2(50)
, prod_subcategory_desc    VARCHAR2(2000)
, prod_category            VARCHAR2(50)
, prod_category_desc       VARCHAR2(2000)
, prod_weight_class        NUMBER(2)
, prod_unit_of_measure     VARCHAR2(20)
, prod_pack_size           VARCHAR2(30)
, supplier_id              NUMBER(6)
, prod_status              VARCHAR2(20)
, prod_list_price          NUMBER(8,2)
, prod_min_price           NUMBER(8,2)
);
/
CREATE TYPE product_t_table AS TABLE OF product_t;
/
COMMIT;

CREATE OR REPLACE PACKAGE cursor_PKG AS
TYPE product_t_rec IS RECORD (
prod_id                   NUMBER(6)
, prod_name                 VARCHAR2(50)
, prod_desc                 VARCHAR2(4000)
, prod_subcategory          VARCHAR2(50)
, prod_subcategory_desc     VARCHAR2(2000)
, prod_category             VARCHAR2(50)
, prod_category_desc        VARCHAR2(2000)
, prod_weight_class         NUMBER(2)
, prod_unit_of_measure      VARCHAR2(20)
, prod_pack_size            VARCHAR2(30)
, supplier_id               NUMBER(6)
, prod_status               VARCHAR2(20)
, prod_list_price           NUMBER(8,2)
, prod_min_price            NUMBER(8,2));
TYPE product_t_rectab IS TABLE OF product_t_rec;
TYPE strong_refcur_t IS REF CURSOR RETURN product_t_rec;
TYPE refcur_t IS REF CURSOR;
END;
/

REM artificial help table, used later
CREATE TABLE obsolete_products_errors (prod_id NUMBER, msg VARCHAR2(2000));

次の例は、prod_category Electronicsを除くすべての廃止製品を表示する単純なフィルタ処理です。このテーブル・ファンクションは、結果セットとしてレコード・セットを戻し、入力として弱い型指定を持つREF CURSORを使用しています。

CREATE OR REPLACE FUNCTION obsolete_products(cur cursor_pkg.refcur_t)
RETURN product_t_table
IS
prod_id                   NUMBER(6);
prod_name                 VARCHAR2(50);
prod_desc                 VARCHAR2(4000);
prod_subcategory          VARCHAR2(50);
prod_subcategory_desc     VARCHAR2(2000);
prod_category             VARCHAR2(50);
prod_category_desc        VARCHAR2(2000);
prod_weight_class         NUMBER(2);
prod_unit_of_measure      VARCHAR2(20);
prod_pack_size            VARCHAR2(30);
supplier_id               NUMBER(6);
prod_status               VARCHAR2(20);
prod_list_price           NUMBER(8,2);
prod_min_price            NUMBER(8,2);
sales NUMBER:=0;
objset product_t_table := product_t_table();
i NUMBER := 0;
BEGIN
LOOP
-- Fetch from cursor variable
FETCH cur INTO prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc, prod_weight_class,
prod_unit_of_measure, prod_pack_size, supplier_id, prod_status,
prod_list_price, prod_min_price;
EXIT WHEN cur%NOTFOUND; -- exit when last row is fetched
-- Category Electronics is not meant to be obsolete and will be suppressed
IF prod_status='obsolete' AND prod_category != 'Electronics' THEN
-- append to collection
i:=i+1;
objset.extend;
objset(i):=product_t( prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc,
prod_weight_class, prod_unit_of_measure, prod_pack_size, supplier_id,
prod_status, prod_list_price, prod_min_price);
END IF;
END LOOP;
CLOSE cur;
RETURN objset;
END;
/

このテーブル・ファンクションをSQL文に使用すると、次の結果が表示されます。ここでは、出力用にSQLの機能を追加使用しています。

SELECT DISTINCT UPPER(prod_category), prod_status
FROM TABLE(obsolete_products(
CURSOR(SELECT prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc, prod_weight_class,
prod_unit_of_measure, prod_pack_size,
supplier_id, prod_status, prod_list_price, prod_min_price
FROM products)));

次の例では、前述の例と同じフィルタ処理を実装しています。両者の主な違いは、次のとおりです。

- この例では、強い型指定を持つREF CURSORを入力として使用しており、次に示す例の1つのように、そのカーソルのオブジェクトに基づいてパラレル化できます。

- テーブル・ファンクションは、レコードの作成直後に段階的に結果セットを戻します。

CREATE OR REPLACE FUNCTION
obsolete_products_pipe(cur cursor_pkg.strong_refcur_t) RETURN product_t_table
PIPELINED
PARALLEL_ENABLE (PARTITION cur BY ANY) IS
prod_id                 NUMBER(6);
prod_name               VARCHAR2(50);
prod_desc               VARCHAR2(4000);
prod_subcategory        VARCHAR2(50);
prod_subcategory_desc   VARCHAR2(2000);
prod_category           VARCHAR2(50);
prod_category_desc      VARCHAR2(2000);
prod_weight_class       NUMBER(2);
prod_unit_of_measure   VARCHAR2(20);
prod_pack_size         VARCHAR2(30);
supplier_id            NUMBER(6);
prod_status            VARCHAR2(20);
prod_list_price        NUMBER(8,2);
prod_min_price         NUMBER(8,2);
sales NUMBER:=0;
BEGIN
LOOP
-- Fetch from cursor variable
FETCH cur INTO prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc,
prod_weight_class, prod_unit_of_measure, prod_pack_size, supplier_id,
prod_status, prod_list_price, prod_min_price;
EXIT WHEN cur%NOTFOUND; -- exit when last row is fetched
IF prod_status='obsolete' AND prod_category !='Electronics' THEN
PIPE ROW (product_t( prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc, prod_weight_class,
prod_unit_of_measure, prod_pack_size, supplier_id, prod_status,
prod_list_price, prod_min_price));
END IF;
END LOOP;
CLOSE cur;
RETURN;
END;
/

次のようなテーブル・ファンクションを使用できます。

SELECT DISTINCT prod_category,
DECODE(prod_status,'obsolete','NO LONGER AVAILABLE','N/A')
FROM TABLE(obsolete_products_pipe(
CURSOR(SELECT prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc,
prod_weight_class, prod_unit_of_measure, prod_pack_size,
supplier_id, prod_status, prod_list_price, prod_min_price
FROM products)));

ここで、入力表productsの並列度を変更し、再度同じ文を発行します。

ALTER TABLE products PARALLEL 4;

セッション統計は、文がパラレル化されていることを示します。

SELECT * FROM V$PQ_SESSTAT WHERE statistic='Queries Parallelized';

STATISTIC              LAST_QUERY  SESSION_TOTAL
--------------------   ----------  -------------
Queries Parallelized            1              3

1 row selected.

テーブル・ファンクションでは、結果を永続表の構造に分岐させることもできます。これを次の例に示します。誤ってステータスobsoleteに設定されている特定のprod_category(デフォルトはElectronics)の製品を除き、それ以外のすべての廃止製品が関数のフィルタにより戻されます。テーブル・ファンクションの結果セットは、他のすべての廃止製品カテゴリで構成されます。検出された間違ったprod_idは、別個の表構造obsolete_products_errorに格納されます。テーブル・ファンクションが自律型トランザクションの一部である場合、サブプログラム呼出しでのエラーを回避するために、各PIPE ROW文の前にCOMMITまたはROLLBACKを実行する必要があることに注意してください。その結果セットは、他のすべての廃止製品カテゴリで構成されます。さらに、通常の変数をテーブル・ファンクションとともに使用する方法を示します。

CREATE OR REPLACE FUNCTION obsolete_products_dml(cur cursor_pkg.strong_refcur_t,
prod_cat varchar2 DEFAULT 'Electronics') RETURN product_t_table
PIPELINED
PARALLEL_ENABLE (PARTITION cur BY ANY) IS
PRAGMA AUTONOMOUS_TRANSACTION;
prod_id                   NUMBER(6);
prod_name                 VARCHAR2(50);
prod_desc                 VARCHAR2(4000);
prod_subcategory          VARCHAR2(50);
prod_subcategory_desc     VARCHAR2(2000);
prod_category             VARCHAR2(50);
prod_category_desc        VARCHAR2(2000);
prod_weight_class         NUMBER(2);
prod_unit_of_measure      VARCHAR2(20);
prod_pack_size            VARCHAR2(30);
supplier_id               NUMBER(6);
prod_status               VARCHAR2(20);
prod_list_price           NUMBER(8,2);
prod_min_price            NUMBER(8,2);
sales                     NUMBER:=0;
BEGIN
LOOP
-- Fetch from cursor variable
FETCH cur INTO prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc, prod_weight_class,
prod_unit_of_measure, prod_pack_size, supplier_id, prod_status,
prod_list_price, prod_min_price;
EXIT WHEN cur%NOTFOUND; -- exit when last row is fetched
IF prod_status='obsolete' THEN
IF prod_category=prod_cat THEN
INSERT INTO obsolete_products_errors VALUES
(prod_id, 'correction: category '||UPPER(prod_cat)||' still
available');
COMMIT;
ELSE
PIPE ROW (product_t( prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc, prod_weight_class,
prod_unit_of_measure, prod_pack_size, supplier_id, prod_status,
prod_list_price, prod_min_price));
END IF;
END IF;
END LOOP;
CLOSE cur;
RETURN;
END;
/

次の問合せでは、誤ってステータスobsoleteに設定されているprod_category Electronicsを除き、すべての廃止製品グループが示されます。

SELECT DISTINCT prod_category, prod_status FROM TABLE(obsolete_products_dml(
CURSOR(SELECT prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc, prod_weight_class,
prod_unit_of_measure, prod_pack_size, supplier_id, prod_status,
prod_list_price, prod_min_price
FROM products)));

このように、prod_category Electronicsには、誤って廃止された製品があります。

SELECT DISTINCT msg FROM obsolete_products_errors;

2番目の入力変数を利用すると、Electronics以外の対象となる別の製品グループを指定できます。

SELECT DISTINCT prod_category, prod_status
FROM TABLE(obsolete_products_dml(
CURSOR(SELECT prod_id, prod_name, prod_desc, prod_subcategory,
prod_subcategory_desc, prod_category, prod_category_desc, prod_weight_class,
prod_unit_of_measure, prod_pack_size, supplier_id, prod_status,
prod_list_price, prod_min_price
FROM products),'Photo'));

テーブル・ファンクションは通常の表と同様に使用できるため、次のようにネストできます。

SELECT DISTINCT prod_category, prod_status
FROM TABLE(obsolete_products_dml(CURSOR(SELECT *
FROM TABLE(obsolete_products_pipe(CURSOR(SELECT prod_id, prod_name, prod_desc,
prod_subcategory, prod_subcategory_desc, prod_category, prod_category_desc,
prod_weight_class, prod_unit_of_measure, prod_pack_size, supplier_id,
prod_status, prod_list_price, prod_min_price
FROM products))))));

Oracle DatabaseのETLの最大のメリットはツールキット機能です。この機能を後述の機能と組み合せると、ETL処理を改善してスピードアップできます。たとえば、入力として外部表を使用し、それを既存の表と結合し、パラレル化されたテーブル・ファンクションの入力に使用して、複雑なビジネス・ロジックを処理できます。このテーブル・ファンクションはMERGE操作の入力ソースとして使用できるため、フラット・ファイルとして提供されたデータ・ウェアハウス用の新規情報をストリーム化し、ETLプロセスを通じて単一の文中で処理できます。

テーブル・ファンクションおよびPL/SQLプログラミングの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。その他の言語で実装されるテーブル・ファンクションの詳細は、『Oracle Databaseデータ・カートリッジ開発者ガイド』を参照してください。

エラーのロギングおよび処理のメカニズム

外部ソースなど、特に様々なソースからのデータを処理する場合、データをロードおよび変換する際に、制約に従っていないデータに遭遇する場合がよくあります。この不適切なデータによって、長時間のロードや変換の操作が中断させられると、多くの時間とリソースが無駄になります。以降の項では、エラーの2つの主な原因とその対処方法について説明します。

- ビジネス・ルールの違反

- データ・ルールの違反(データ・エラー)

ビジネス・ルールの違反

論理的に制約に従っていないデータは、データを使用する前に認識されているビジネス・ルールに違反します。ほとんどの場合、この種のエラー処理は、ロードまたは変換プロセスに組み込まれます。ただし、すべてのレコードに対するエラー識別にかかるコストが膨大となり、かつデータ・ルール違反であってもビジネス・ルールが適用される可能性があるような状況(数百の列をテストして、それらがNOT NULLかどうかを確認するなど)では、プログラマは多くの場合、既知の論理エラーの場合でも一般的に処理する方を選択します。この例は、「データ・エラーのシナリオ」に示しています。

論理的なルールの組込みは、簡単な場合(データの入力ストリームに対してフィルタ条件を適用するなど)もあれば、複雑な場合(不適切なデータを異なる変換ワークフローに送り込むなど)もあります。次に、例をいくつか示します。

- SQLを使用した論理データ・エラーのフィルタ処理。特定の条件を満たさないデータを、処理する前にフィルタにかけて除外します。

- 論理データ・エラーの識別と選別。これは、複雑なケースでは、例15-6「テーブル・ファンクションの基本」に示すようにプロシージャによるアプローチで、単純なケースでは、例15-1「SQLを使用したマージ操作」に示すようにSQLを使用して行うことができます。

データ・ルールの違反(データ・エラー)

論理エラーと異なり、データ・ルールの違反は、ロード・プロセスや変換プロセスでは通常想定していません。操作では処理されないこのような想定外のデータ・ルールの違反(データ・エラー)によって、処理は失敗します。データ・ルールの違反は、データベース内で発生し、文を失敗させるエラー状況です。この例として、データ型の変換エラーや制約違反があります。

かつてのSQLでは、大量処理の一部として行レベルでデータ・エラーを処理する方法がありませんでした。データベース内のデータ・エラーを処理する唯一の手段は、PL/SQLを使用することでした。現在では、DML操作の続行中でもデータ・エラーのログを特別なエラー表に記録できるようになりました。

以降の項では、PL/SQLおよびDMLエラー・ロギング表を使用した例外処理について簡単に説明します。

PL/SQLを使用したデータ・エラーの処理

次の文は、PL/SQLを使用してエラー処理を行う方法の例を示しています。ここでは、すべてのエラーを捕捉するために、プロシージャによるレコードレベルの処理を使用する必要があります。この文は、「エラー・ロギング表を使用したデータ・エラーの処理」で説明している文とほぼ同じです。

DECLARE
errm number default 0;
BEGIN
FOR crec IN (SELECT product_id, customer_id, TRUNC(sales_date) sd,
promotion_id, quantity, amount
FROM sales_activity_direct) loop

BEGIN
INSERT INTO sales VALUES (crec.product_id, crec.customer_id,
crec.sd, 3, crec.promotion_id,
crec.quantity, crec.amount);
exception
WHEN others then
errm := sqlerrm;
INSERT INTO sales_activity_error
VALUES (errm, crec.product_id, crec.customer_id, crec.sd,
crec.promotion_id, crec.quantity, crec.amount);
END;
END loop;
END;
/

エラー・ロギング表を使用したデータ・エラーの処理

DMLエラー・ロギングによって既存のDML機能が拡張され、ユーザーがエラー・ロギング表の名前を指定すると、DML操作中に検出されたエラーがOracle Databaseによってその表に記録されるようになりました。これにより、どのようなエラーが検出されてもDML操作を完了でき、後でエラーのある行において対処措置をとることができます。

このDMLエラー・ロギング表は、ターゲット列に起こり得るエラーを格納可能なデータ型を使用した、DML操作のターゲット表の列のすべてまたはサブセットを表すユーザー定義の列のセット、およびいくつかの必須の制御列から構成されています。たとえば、ターゲット表のNUMBER列のTO_NUMデータ型変換エラーを格納するには、エラー・ロギング表にVARCHAR2データ型が必要となります。DMLエラー・ロギング表を作成するには、DBMS_ERRLOGパッケージを使用する必要があります。このパッケージおよびロギング表の構造の詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

DMLターゲット表とエラー・ロギング表の間の列名のマッピングによって、制御列以外のどの列がDML操作でロギングされるかが決まります。

次の文は、DMLエラー・ロギングによって「SQLを使用したデータの変換」の例を強化する方法を示します。

INSERT /*+ APPEND PARALLEL */
INTO sales SELECT product_id, customer_id, TRUNC(sales_date), 3,
promotion_id, quantity, amount
FROM sales_activity_direct
LOG ERRORS INTO sales_activity_errors('load_20040802')
REJECT LIMIT UNLIMITED

すべてのデータ・エラーのログが、オプションのタグload_20040802で識別される表sales_activity_errorsに記録されます。このINSERT文は、データ・エラーが存在する場合でも成功します。この文を使用する前にDMLエラー・ロギング表を作成しておく必要がある点に注意してください。

REJECT LIMIT Xを指定していた場合、この文は失敗し、エラー・メッセージ「エラーX=1」が示されます。このエラー・メッセージは、拒否制限(REJECT LIMIT)によって異なる場合があります。文が失敗した場合、DML文のみがロールバックされ、DMLエラー・ロギング表への挿入操作はロールバックされません。エラー・ロギング表には、X+1個の行が含まれます。

DMLエラー・ロギング表は、実行ユーザーとは異なるスキーマに配置できますが、その場合、表の完全な名前を指定する必要があります。状況に応じてDMLエラー・ロギング表の名前を省略することもできます。その場合、DBMS_ERRLOGパッケージによって生成される表のデフォルト名が使用されます。

次に、DML操作中にログに書き込まれるエラーを示します。

- 大きすぎる列値

- 制約違反(NOT NULL制約、一意制約、参照制約およびチェック制約)

- トリガー実行中に発生したエラー

- 副問合せの列と表の対応列との間の型変換で発生したエラー

- パーティション・マッピング・エラー

次の条件下ではエラー・ロギング機能が機能せず、文は失敗してロールバックします。

- 遅延制約の違反

- 領域不足エラー

- 一意制約や索引の違反を招くすべてのINSERT操作(INSERTまたはMERGE)

- 一意制約や索引の違反を招くすべてのUPDATE操作(UPDATEまたはMERGE)

また、LONG、LOBまたはオブジェクト型の列については、エラー・ロギング表においてエラーを追跡できません。エラー・ロギングを使用する際の制約の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

DMLエラー・ロギングは、あらゆる種類のDML操作に適用できます。次の項では、これらの例について説明します。

外部ロード・ユーティリティとしてのSQL*Loaderにも、データ・エラーのロギング機能がありますが、データベース内部に統合されたETL処理のメリットがありません。

ロードおよび変換の使用例

ここでは、典型的なロードおよび変換タスクの例を示します。

- キー参照のシナリオ

- ビジネス・ルール違反のシナリオ

- データ・エラーのシナリオ

- ビジネス・ルール違反のシナリオ

- ピボットのシナリオ

キー参照のシナリオ

典型的な変換として、キー参照があります。たとえば、売上トランザクション・データが小売データ・ウェアハウスにロードされているとします。データ・ウェアハウスのsales表にはproduct_id列がありますが、ソース・システムから抽出された売上トランザクション・データには、製品IDではなくUPCコード(Uniform Price Codes)があります。そのため、新しい売上トランザクション・データをsales表に挿入できるようにするには、最初にUPCコードを製品IDに変換する必要があります。

この変換を実行するには、product_id値をUPCコードに関係付ける参照表が必要です。この表は、productディメンション表か、またはこの変換をサポートするために特別に作成された、データ・ウェアハウスにある別の表です。この例では、product_id列およびupc_code列を持つ、productという表があると想定しています。

このデータ置換による変換は、次のCTAS文を使用して実装できます。

CREATE TABLE temp_sales_step2 NOLOGGING PARALLEL AS SELECT sales_transaction_id,
product.product_id sales_product_id, sales_customer_id, sales_time_id,
sales_channel_id, sales_quantity_sold, sales_dollar_amount
FROM  temp_sales_step1, product
WHERE temp_sales_step1.upc_code = product.upc_code;

このCTAS文は、有効な各UPCコードを、有効なproduct_id値に変換します。各UPCコードが有効であることをETLプロセスが保証している場合は、この文のみで変換全体を実装できます。

ビジネス・ルール違反のシナリオ

前述の例で、有効なUPCコードが付いていない新規の売上データ(論理データ・エラー)も処理する必要がある場合は、次のようにCTAS文を追加使用して無効な行を識別できます。

CREATE TABLE temp_sales_step1_invalid NOLOGGING PARALLEL AS
SELECT * FROM temp_sales_step1 s
WHERE NOT EXISTS (SELECT 1 FROM product p WHERE p.upc_code=s.upc_code);

無効なデータは別の表temp_sales_step1_invalidに格納され、ETLプロセスで別々に処理できます。

無効なデータを処理する別の方法として、次の文に示すように、元のCTASを変更して外部結合を使用します。

CREATE TABLE temp_sales_step2 NOLOGGING PARALLEL AS
SELECT sales_transaction_id, product.product_id sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id, sales_quantity_sold,
sales_dollar_amount
FROM  temp_sales_step1, product
WHERE temp_sales_step1.upc_code = product.upc_code (+);

外部結合を使用すると、無効なUPCコードを含んでいた売上トランザクションに、NULLのproduct_idが割り当てられます。これらのトランザクションは後で処理できます。または、NULLのproduct_idで値を別の表に分離するマルチテーブル・インサートも使用できます。これは、予想されたエラー数がデータ全体の量と比較して相対的に少ないときに有効なアプローチとなる場合があります。後続の処理では、大きなターゲット表を扱う必要がなくなり、小さな表のみを扱うことになります。

INSERT /*+ APPEND PARALLEL */ FIRST
WHEN sales_product_id IS NOT NULL THEN
INTO temp_sales_step2
VALUES (sales_transaction_id, sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id,
sales_quantity_sold, sales_dollar_amount)
ELSE
INTO temp_sales_step1_invalid
VALUES (sales_transaction_id, sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id,
sales_quantity_sold, sales_dollar_amount)
SELECT sales_transaction_id, product.product_id sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id,
sales_quantity_sold, sales_dollar_amount
FROM temp_sales_step1, product
WHERE temp_sales_step1.upc_code = product.upc_code (+);

このソリューションでは、空の表temp_sales_step2およびtemp_sales_step1_invalidがあらかじめ存在している必要がある点に注意してください。

無効なUPCコードを処理するには、他にも方法があります。あるデータ・ウェアハウスではNULL値のproduct_idをsales表に挿入するように選択されます。また、無効なUPCコードがすべて処理されるまで、バッチ全体のどの新規データもsales表に挿入できないデータ・ウェアハウスもあります。どの方法が適切かは、データ・ウェアハウスのビジネス要件によって決まります。特定の要件に関係なく、例外処理は、変換と同じく基本的なSQLによって処理されます。

データ・エラーのシナリオ

データの質が不明の場合、前述の項で示した例を、次に示すように想定外のデータ・エラー(データ型の変換エラーなど)を処理するように強化できます。

INSERT /*+ APPEND PARALLEL */ FIRST
WHEN sales_product_id IS NOT NULL THEN
INTO temp_sales_step2
VALUES (sales_transaction_id, sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id,
sales_quantity_sold, sales_dollar_amount)
LOG ERRORS INTO sales_step2_errors('load_20040804')
REJECT LIMIT UNLIMITED
ELSE
INTO temp_sales_step1_invalid
VALUES (sales_transaction_id, sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id,
sales_quantity_sold, sales_dollar_amount)
LOG ERRORS INTO sales_step2_errors('load_20040804')
REJECT LIMIT UNLIMITED
SELECT sales_transaction_id, product.product_id sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id,
sales_quantity_sold, sales_dollar_amount
FROM temp_sales_step1, product
WHERE temp_sales_step1.upc_code = product.upc_code (+);

この文は、表temp_sales_step1_invalidに有効な製品UPCコードが存在しないという論理データ・エラー、およびsales_step2_errorsという名前のDMLエラー・ロギング表において可能性のあるその他のすべてのエラーを追跡します。エラー・ロギング表は、複数のDML操作で使用できます。

または、NOT NULL制約を使用して、データベース・レベルで有効なUPCコードが存在するというビジネス・ルールを実施する方法もあります。外部結合を使用すると、有効なUPCコードを持たないすべての注文はNULL値にマップされ、データ・エラーとして扱われます。次の文では、これらのエラーを追跡するために、このDMLエラー・ロギング機能が使用されています。

INSERT /*+ APPEND PARALLEL */
INTO temp_sales_step2
VALUES (sales_transaction_id, sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id,
sales_quantity_sold, sales_dollar_amount)
SELECT sales_transaction_id, product.product_id sales_product_id,
sales_customer_id, sales_time_id, sales_channel_id,
sales_quantity_sold, sales_dollar_amount
FROM temp_sales_step1, product
WHERE temp_sales_step1.upc_code = product.upc_code (+)
LOG ERRORS INTO sales_step2_errors('load_20040804')
REJECT LIMIT UNLIMITED;

エラー・ロギング表には、DML操作の失敗の原因となったすべてのレコードが格納されます。この内容を利用して、あらゆるエラーを分析および修正できます。エラー・ロギング表の内容は、DML操作自体が成功したかどうかに関わりなく、すべてのDML操作に対して保持されます。拒否制限(REJECT LIMIT)に達したため、次のSQL文が失敗したものと仮定します。

SQL> INSERT /*+ APPEND NOLOGGING PARALLEL */ INTO sales_overall
2 SELECT * FROM sales_activity_direct
3 LOG ERRORS INTO err$_sales_overall ('load_test2')
4 REJECT LIMIT 10;
SELECT * FROM sales_activity_direct
*
ERROR at line 2:
ORA-01722: invalid number

エラー・ロギング表の名前err$_sales_overallは、DBMS_ERRLOGパッケージを使用して導出されたデフォルトの名前です。詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

生成されたエラー・メッセージは、最初のエラー制限に達した後で発生しています。次のエラー(番号11)は、エラーを生じさせたものです。表示されているエラー・メッセージは、制限を超過したエラーを基準としているため、たとえば、9番目のエラーは、11番目のエラーとは異なる場合があります。

ターゲット表sales_overallには入力されているレコードがありません(空の表だったと考えられる)が、エラー・ロギング表には、11個(REJECT LIMIT + 1)の行が含まれます。

SQL> SELECT COUNT(*) FROM sales_overall;
COUNT(*)
----------
0

SQL> SELECT COUNT(*) FROM err$_sales_overall;
COUNT(*)
----------
11

DMLエラー・ロギング表は、すべてのエラー・ロギング表に必須であるいくつかの固定の制御列で構成されます。Oracleエラー番号の他に、エラー・メッセージも格納されます。多くの場合、エラー・メッセージにより、データ・エラーの根本的原因を分析および解決するための詳細な情報が示されます。次のDMLエラー・ロギング表のSQL出力は、この違いを示しています。2番目の出力には、NOT NULL違反によって拒否された行の追加情報が含まれています。

SQL> SELECT DISTINCT ora_err_number$ FROM err$_sales_overall;

ORA_ERR_NUMBER$
---------------
1400
1722
1830
1847

SQL> SELECT DISTINCT ora_err_number$, ora_err_mesg$ FROM err$_sales_overall;

ORA_ERR_NUMBER$       ORA_ERR_MESG$
1400       ORA-01400: cannot insert NULL into
("SH"."SALES_OVERALL"."CUST_ID")
1400       ORA-01400: cannot insert NULL into
("SH"."SALES_OVERALL"."PROD_ID")
1722       ORA-01722: invalid number
1830       ORA-01830: date format picture ends before
converting entire input string
1847       ORA-01847: day of month must be between 1 and last
day of month

制御列の詳細は、『Oracle Database管理者ガイド』を参照してください。

ピボットのシナリオ

データ・ウェアハウスは、多数の異なるソースからデータを受け取ることができます。これらのソース・システムには、リレーショナル・データベースではないものもあり、データ・ウェアハウスとは大きく異なるフォーマットでデータが格納されている場合があります。たとえば、売上レコードの集合を、次のフォームの非リレーショナル・データベースから受け取ったとします。

product_id, customer_id, weekly_start_date, sales_sun, sales_mon, sales_tue,
sales_wed, sales_thu, sales_fri, sales_sat

入力表は次のようになります。

SELECT * FROM sales_input_table;

PRODUCT_ID CUSTOMER_ID WEEKLY_ST  SALES_SUN  SALES_MON  SALES_TUE  SALES_WED SALES_THU  SALES_FRI  SALES_SAT
---------- ----------- --------- ---------- ---------- ---------- -------------------- ---------- ----------
111         222 01-OCT-00        100        200        300        400       500        600        700
222         333 08-OCT-00        200        300        400        500       600        700        800
333         444 15-OCT-00        300        400        500        600       700        800        900

データ・ウェアハウスでは、次のような一般的なリレーショナル形式でshサンプル・スキーマのファクト表salesにこれらのレコードを格納します。

prod_id, cust_id, time_id, amount_sold

注意:
この例では、簡潔にするために表の多数の列を無視しているため、sales表の多数の制約が使用禁止になっています。

これには、入力ストリームの各レコードが、データ・ウェアハウスのsales表の7つのレコードに変換されるように変換処理を作成する必要があります。通常、この操作はピボットと呼ばれ、Oracle Databaseでは複数の方法でこの操作を行えます。

前述の例の結果は、次のようになります。

SELECT prod_id, cust_id, time_id, amount_sold FROM sales;

PROD_ID    CUST_ID   TIME_ID   AMOUNT_SOLD
---------- ----------   --------- -----------
111        222   01-OCT-00         100
111        222   02-OCT-00         200
111        222   03-OCT-00         300
111        222   04-OCT-00         400
111        222   05-OCT-00         500
111        222   06-OCT-00         600
111        222   07-OCT-00         700
222        333   08-OCT-00         200
222        333   09-OCT-00         300
222        333   10-OCT-00         400
222        333   11-OCT-00         500
222        333   12-OCT-00         600
222        333   13-OCT-00         700
222        333   14-OCT-00         800
333        444   15-OCT-00         300
333        444   16-OCT-00         400
333        444   17-OCT-00         500
333        444   18-OCT-00         600
333        444   19-OCT-00         700
333        444   20-OCT-00         800
333        444   21-OCT-00         900

例15-7 ピボット

次の例では、マルチテーブル・インサート構文を使用して、デモ表sh.salesに異なる構造を持つ入力表からデータを挿入しています。マルチテーブルINSERT文は次のようになります。

INSERT ALL INTO sales (prod_id, cust_id, time_id, amount_sold)
VALUES (product_id, customer_id, weekly_start_date, sales_sun)
INTO sales (prod_id, cust_id, time_id, amount_sold)
VALUES (product_id, customer_id, weekly_start_date+1, sales_mon)
INTO sales (prod_id, cust_id, time_id, amount_sold)
VALUES (product_id, customer_id, weekly_start_date+2, sales_tue)
INTO sales (prod_id, cust_id, time_id, amount_sold)
VALUES (product_id, customer_id, weekly_start_date+3, sales_wed)
INTO sales (prod_id, cust_id, time_id, amount_sold)
VALUES (product_id, customer_id, weekly_start_date+4, sales_thu)
INTO sales (prod_id, cust_id, time_id, amount_sold)
VALUES (product_id, customer_id, weekly_start_date+5, sales_fri)
INTO sales (prod_id, cust_id, time_id, amount_sold)
VALUES (product_id, customer_id, weekly_start_date+6, sales_sat)
SELECT product_id, customer_id, weekly_start_date, sales_sun,
sales_mon, sales_tue, sales_wed, sales_thu, sales_fri, sales_sat
FROM sales_input_table;

この文では、ソース表が1回のみスキャンされ、毎日の適切なデータが挿入されます。

関連項目:

- ピボットの詳細は、「ピボット操作」を参照してください。

- pivot_clauseの構文については、『Oracle Database SQL言語リファレンス』を参照してください。

16 データ・ウェアハウスのメンテナンス

この章では、データ・ウェアハウスのロードおよびリフレッシュ方法について説明します。内容は次のとおりです。

- パーティション化によるデータ・ウェアハウス・リフレッシュの改善

- リフレッシュ中のDML操作の最適化

- マテリアライズド・ビューのリフレッシュ

- パーティション表付きマテリアライズド・ビューの使用

パーティション化によるデータ・ウェアハウス・リフレッシュの改善

ETL(抽出、変換、ロード)がスケジュールに基づいて実行され、オリジナルのソース・システムに対して行われた変更が反映されます。このステップ中に、新しいクリーン・データを本番データ・ウェアハウス・スキーマに物理的に挿入し、この新しいデータがエンド・ユーザーにも利用できるように、必要に応じてその他のステップ(索引の作成、制約の妥当性チェック、データのバックアップ作成など)をすべて実行します。このデータがすべてデータ・ウェアハウスにロードされた後に、最新データが反映されるようにマテリアライズド・ビューを更新する必要があります。

データ・ウェアハウスのパーティション化方法によって、データ・ウェアハウスのロード・プロセスにおけるリフレッシュ操作の効率が決まります。実際、データ・ウェアハウスの表および索引をパーティション化する方法を選択するときには、ロード・プロセスが重要な考慮点となることがあります。

非常に大規模なデータ・ウェアハウス表(スター・スキーマのファクト表など)のパーティション化方法は、データ・ウェアハウスのロード・パラダイムをベースにする必要があります。

ほとんどのデータ・ウェアハウスには、新しいデータが定期的にロードされます。たとえば、毎晩、毎週、毎月などのペースで、データ・ウェアハウスに新しいデータが格納されます。週末または月末にロードされるデータは、通常、その週またはその月のトランザクションに対応しています。このように、非常に一般的なケースでは、データ・ウェアハウスは時間ごとにロードされます。そのため、データ・ウェアハウス表には、日付列でのパーティション化が適しています。たとえば、次のデータ・ウェアハウスの例では、新しいデータがsales表に毎月ロードされるとします。また、sales表は月別にパーティション化されているとします。表salesに新しい月(2001年1月)のデータを追加するロード手順は、次のようになります。

1. 別に用意したsales_01_2001表に新しいデータを格納します。このデータは、データ・ウェアハウスの外部から直接sales_01_2001にロードできます。また、前にデータ・ウェアハウスで実行されたデータ変換操作の結果をロードすることも可能です。sales_01_2001の列、データ型などは、sales表と同一です。このsales_01_2001表の統計情報データを収集します。

2. sales_01_2001に索引を作成し、制約を追加します。この場合も、sales_01_2001の索引および制約は、salesの索引および制約と同一とします。索引はパラレルで作成できます。また、NOLOGGINGおよびCOMPUTE STATISTICSオプションを使用する必要があります。次に例を示します。

CREATE BITMAP INDEX sales_01_2001_customer_id_bix
ON sales_01_2001(customer_id)
TABLESPACE sales_idx NOLOGGING PARALLEL 8 COMPUTE STATISTICS;

sales表に存在するすべての制約が、sales_01_2001表に適用される必要があります。これには参照整合性制約も含まれます。典型的な制約の例としては、次のものがあります。

ALTER TABLE sales_01_2001 ADD CONSTRAINT sales_customer_id
REFERENCES customer(customer_id) ENABLE NOVALIDATE;

パーティション表salesにグローバル索引構造により強制適用される主キーまたは一意キーがある場合は、索引構造を作成せずに、次のようにしてsales_pk_jan01の制約が有効になるようにしてください。

ALTER TABLE sales_01_2001 ADD CONSTRAINT sales_pk_jan01
PRIMARY KEY (sales_transaction_id) DISABLE VALIDATE;

ENABLE句で制約を作成すると、パーティション表のローカル索引構造と一致しない一意索引が作成されます。非パーティション表に、パーティション表の既存のグローバル索引と交換される索引構造を作成しないでください。EXCHANGEコマンドが失敗します。

3. sales_01_2001表をsales表に追加します。
この新しいデータをsales表に追加するには、2つの操作が必要です。まず、sales表に新しいパーティションを追加します。これには、ALTER TABLE ... ADD PARTITION文を使用します。これによって、sales表に空のパーティションが追加されます。

ALTER TABLE sales ADD PARTITION sales_01_2001
VALUES LESS THAN (TO_DATE('01-FEB-2001', 'DD-MON-YYYY'));

この後、EXCHANGE PARTITION操作によって、新しく作成した表をこのパーティションに追加できます。この操作によって、新しい空のパーティションが、新しくロードされた表と交換されます。

ALTER TABLE sales EXCHANGE PARTITION sales_01_2001 WITH TABLE sales_01_2001
INCLUDING INDEXES WITHOUT VALIDATION UPDATE GLOBAL INDEXES;

EXCHANGE操作は、sales_01_2001表にすでにあった索引および制約を保存します。一意制約(sales_transaction_idの一意制約など)の場合は、前述のようにUPDATE GLOBAL INDEXES句を使用できます。これにより、グローバル索引構造はパーティション・メンテナンス操作の一部として自動的にメンテナンスされ、プロセス全体でアクセス可能な状態に保たれます。外部キー制約のみの場合は、EXCHANGE操作はすぐに処理を終了します。

このパーティション化テクニックには、重要な利点があります。第1に、新しいデータのロードに使用するリソースが最小限に抑えられます。新しいデータは、完全に別々の表にロードされるため、索引および制約の処理は、その新しいパーティションのみに適用されます。sales表が50GBで、12のパーティションを持つとすると、新しい月のデータ・サイズは約4GBになります。索引を作成する必要があるのは新しい月のデータのみです。残りの46GBのデータに関する索引は、まったく変更する必要はありません。このパーティション化方法では、ロード処理時間は、sales表全体のサイズではなく、新しいデータの量に比例します。

第2に、同時問合せへの影響を最小限に抑えて、新しいデータをロードできます。データのロードに関連する操作は、すべて別のsales_01_2001表に対して発生しています。したがって、sales表の既存のデータや索引はデータのリフレッシュ処理中にはまったく影響を受けません。このリフレッシュ処理の間、sales表およびその索引に、処理が加えられないようにできます。

第3に、グローバル索引が存在する場合は、交換コマンドの一部として段階的にメンテナンスされます。このメンテナンスは、既存のグローバル索引構造の可用性には影響しません。

EXCHANGE操作は、公開機能と見ることができます。データ・ウェアハウスの管理者がsales_01_2001表をsales表に交換するまで、エンド・ユーザーは新しいデータを参照できません。EXCHANGEが実行されると、その直後に、sales表にアクセスするすべてのエンド・ユーザー問合せからsales_01_2001データが参照できるようになります。

パーティション化は、新しいデータの追加のみでなく、データの削除やアーカイブにも有効です。多くのデータ・ウェアハウスがデータのローリング・ウィンドウを保持しています。たとえば、データ・ウェアハウスには最近36か月のsalesデータが格納されているとします。sales表に新しいパーティションを追加できるのと同じように(前述参照)、古いパーティションも即座に(かつ他に影響を及ぼさずに)sales表から削除できます。パーティションの追加には2つの効果(リソース使用の削減およびエンド・ユーザーへの影響の最小化)がありますが、これはパーティションの削除にも当てはまります。

パーティション表からデータを削除しても、必ずしも古いデータがデータベースから物理的に削除されるわけではありません。パーティション表からデータを削除するには、他に次の2つの方法があります。1つ目は、古いデータが含まれているパーティションを削除して割り当てられている領域を解放することで、データベースからすべてのデータを物理的に削除する方法です。

ALTER TABLE sales DROP PARTITION sales_01_1998;

2つ目は、古いパーティションを、同じ構造を持つ空の表と交換する方法です。この空の表は、ロード処理のステップ1と2で説明したのと同じ手順で作成します。新しい空の表スタブの名前をsales_archive_01_1998とすると、次のSQL文でパーティションsales_01_1998が「空に」なります。

ALTER TABLE sales EXCHANGE PARTITION sales_01_1998
WITH TABLE sales_archive_01_1998 INCLUDING INDEXES WITHOUT VALIDATION
UPDATE GLOBAL INDEXES;

古いデータは、交換された非パーティション表sales_archive_01_1998としてまだ存在していることに注意してください。

すべてのパーティションを別々の表領域に格納するという方法でパーティション表が設定されていた場合は、実際のデータ(表領域)を削除する前に、Oracle Databaseのトランスポータブル表領域フレームワークを使用して、この表をアーカイブ(または転送)できます。トランスポータブル表領域の詳細は、「トランスポータブル表領域を使用した転送」を参照してください。

場合によっては、古いデータをすぐに削除するのではなく、パーティション表の一部として保持することが必要な場合があります。このデータはもう重要ではありませんが、この古い読取り専用のデータにアクセスする問合せがまだ存在する可能性があります。古いデータの使用領域を最小化するには、Oracleのデータ圧縮を使用できます。1つ以上の圧縮パーティションがすでにパーティション表の一部に含まれていることも想定されます。

関連項目:

- 表の圧縮の概要は、第3章「データ・ウェアハウスの物理設計」を参照してください。

- パーティション化と表の圧縮については、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

リフレッシュの使用例

典型的な使用例では、古いデータを圧縮するのみでなく、いくつかの古いパーティションを将来のバックアップの最小処理単位にマージすることが必要な場合があります。バックアップ(パーティション)の最小処理単位がどの四半期についても四半期ベースであり、最も古い月と最新の月の差が36か月より多いものとします。この場合、sales_01_1998、sales_02_1998およびsales_03_1998を新しい圧縮パーティションsales_q1_1998に圧縮およびマージすることになります。

1. 別の表領域に新しいマージ・パーティションをパラレルに作成します。パーティションは、次のMERGE操作の一部として圧縮されます。

ALTER TABLE sales MERGE PARTITIONS sales_01_1998, sales_02_1998, sales_03_1998
INTO PARTITION sales_q1_1998 TABLESPACE archive_q1_1998
COMPRESS UPDATE GLOBAL INDEXES PARALLEL 4;

2. パーティションのMERGE操作により、新しいマージ・パーティションに対するローカル索引は無効になります。したがって、再作成する必要があります。

ALTER TABLE sales MODIFY PARTITION sales_q1_1998
REBUILD UNUSABLE LOCAL INDEXES;

かわりに、パーティション表の外部に新しい圧縮表を作成して、これと交換する方法も選択できます。どちらの方法でも、パフォーマンスと一時領域の使用量は同程度です。

1. マージされた新しい情報を格納する中間的な表を作成します。次の文は、デフォルトで、元の表からNOT NULL制約をすべて継承します。

CREATE TABLE sales_q1_1998_out TABLESPACE archive_q1_1998
NOLOGGING COMPRESS PARALLEL 4 AS SELECT * FROM sales
WHERE time_id >=  TO_DATE('01-JAN-1998','dd-mon-yyyy')
AND time_id < TO_DATE('01-APR-1998','dd-mon-yyyy');

2. 既存の表salesの他に、表sales_q1_1998_outに対して等価な索引構造を作成します。

3. 既存の表salesを、新しい圧縮表sales_q1_1998_outと交換するために準備します。交換される表には、実際には3つのパーティションにまたがるデータが含まれているため、参照中のレンジ境界を持つ、一致するパーティションを1つ作成する必要があります。既存のパーティションを2つ削除するだけで済みます。低い方のパーティションsales_01_1998とsales_02_1998を削除する必要があることに注意してください。レンジ・パーティションの下限は、常に前のパーティションの上限(上限を含まない)によって定義されます。

ALTER TABLE sales DROP PARTITION sales_01_1998;
ALTER TABLE sales DROP PARTITION sales_02_1998;

4. これで、表sales_q1_1998_outをパーティションsales_03_1998と交換できます。パーティションの名前から想定されるものとは異なり、この境界は1998年の第1四半期をカバーします。

ALTER TABLE sales EXCHANGE PARTITION sales_03_1998
WITH TABLE sales_q1_1998_out INCLUDING INDEXES WITHOUT VALIDATION
UPDATE GLOBAL INDEXES;

どちらの方法も、多少異なるビジネス・シナリオに適用されます。MERGE PARTITIONを使用する方法では、影響を受けるパーティションのローカル索引構造は無効になりますが、データは常時アクセス可能なまま保たれます。影響を受けるパーティションに対して、使用できない索引構造の1つを介してアクセスしようとすると、エラーが発生します。使用が制限される時間は、ローカル・ビットマップ索引構造を再作成するための時間とほぼ同程度になります。ほとんどの場合、これは無視できます。パーティション表のこの部分はそれほど頻繁にアクセスされないためです。

ただし、CTAS方法では索引構造が使用できない時間はゼロに近く短縮されますが、パーティション表にすべてのデータが揃わない特定の時間枠があります。これは、2つのパーティションを削除したためです。使用が制限される時間は、表を交換するための時間とほぼ同程度になります。グローバル索引の有無およびその数によって、この時間枠は変わります。既存のグローバル索引がない場合、この時間枠はほんの数秒です。

これらの例は、データ・ウェアハウスのローリング・ウィンドウのロードの使用例を単純化したものです。実際のデータ・ウェアハウスのリフレッシュ特性は、さらに複雑です。ただし、このローリング・ウィンドウを使用すると、リフレッシュ特性がさらに複雑になっても、十分な効果を得ることができます。

パーティション表に単一または複数の圧縮パーティションを初めて追加するときは、その前にローカル・ビットマップ索引をすべて削除するか使用不可にマークする必要があることに注意してください。最初に圧縮パーティションを追加した後は、圧縮パーティションに関するその後のどの操作においても追加の処置は必要ありません。これは、どのような方法で圧縮表に圧縮パーティションが追加されたかには関係ありません。

関連項目:

- パーティション化と表の圧縮については、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

- パーティション化と表の圧縮の詳細は、『Oracle Database管理者ガイド』を参照してください。

データ・ウェアハウスのリフレッシュにパーティション化を使用する使用例

ここでは、リフレッシュにパーティション化を使用する一般的な例を2つ紹介します。

リフレッシュ使用例1

データは毎日ロードされます。ただし、データ・ウェアハウスには2年分のデータを格納するため、1日単位のパーティションは適切ではありません。

ソリューションは、週別または月別(適切な方)にパーティション化することです。INSERTを使用して、新しいデータを既存のパーティションに追加します。INSERT操作が影響するパーティションは1つのみなので、前述の利点はそのまま残ります。INSERT操作は、パーティションが表の一部である場合に行うことができます。単一パーティションへのINSERTはパラレル化できます。

INSERT /*+ APPEND*/ INTO sales PARTITION (sales_01_2001)
SELECT * FROM new_sales;

このsalesパーティションの索引もパラレルでメンテナンスされます。このメソッドのかわりにEXCHANGE操作を使用することもできます。そのためには、sales表のsales_01_2001パーティションを交換し、INSERT操作を使用します。この方法を使用するのは、索引をメンテナンスするよりも、削除して再作成する方が効率的な場合です。

リフレッシュ使用例2

新しいデータは、主に最近の日、週、月などのものから構成されますが、以前の期間のデータも含まれます。

ソリューション1

パラレルSQL操作(CREATE TABLE ... AS SELECTなど)を使用して、新しいデータを以前の期間のデータから分離します。日付が古いデータは、他のテクニックを使用して別に処理します。

新しいデータは、必ず時間ベースであるとは限りません。データ・ウェアハウスがビジネス・ニーズに基づいて複数の業務系システムから新規データを受け取るようにすることもできます。たとえば、直接チャネルからの売上データが、間接チャネルからのデータとは別にデータ・ウェアハウスに格納されることもあります。また、業務上の理由から、直接データと間接データを別のパーティションに保存することも適しています。

ソリューション2

Oracleでは、コンポジット・レンジ-リスト・パーティション化をサポートしています。sales表の主要なパーティション化方法は、例に示すようにtime_idに基づいたレンジ・パーティション化にできます。ただし、サブパーティション化は、チャネル属性に基づいたリストです。これで各サブパーティションを互いに(それぞれ別個のチャネルごとに)独立にロードして、前述のローリング・ウィンドウ操作で追加できます。パーティション化方式は、最も最適化された方法でビジネス・ニーズを処理します。

リフレッシュ中のDML操作の最適化

DMLパフォーマンスは、次の方法で最適化できます。

- 効率的なMERGE操作の実装

- 参照整合性の保持

- データの削除

効率的なMERGE操作の実装

ソース・システムから抽出したデータは、データ・ウェアハウスに挿入する必要のある新しいレコードの単なるリストではありません。この新しいデータセットは、新しいレコードと変更レコードの組合せで構成されます。たとえば、OLTPシステムから抽出したデータのほとんどが新しい売上トランザクションによるものだとします。これらのレコードは、ウェアハウスのsales表に挿入されますが、その中には、商品の返品や、最初にデータ・ウェアハウスにロードしたときに不備があったトランザクションの修正など、以前のトランザクションに対する変更を反映しているものがある場合があります。このようなレコードについては、sales表の更新が必要です。

new_sales表があり、この表にsales表に適用される挿入項目と更新項目の両方が格納されている例を考えてみます。データ・ウェアハウスのロード・プロセス全体を設計するときに、次のような処理方法で、このnew_sales表にレコードを格納することにします。

- new_sales表のレコードの任意のsales_transaction_idがsales表にすでに存在する場合、new_sales表からsales_dollar_amountおよびsales_quantity_soldの値をsales表の既存の行に追加することで、sales表を更新します。

- それ以外の場合は、new_sales表から新しいレコード全体をsales表に挿入します。

このUPDATE-ELSE-INSERT操作は、通常はマージと呼ばれます。マージは、1つのSQL文で実行できます。

例16-1 MERGE操作

MERGE INTO sales s USING new_sales n
ON (s.sales_transaction_id = n.sales_transaction_id)
WHEN MATCHED THEN
UPDATE SET s.sales_quantity_sold = s.sales_quantity_sold + n.sales_quantity_sold,
s.sales_dollar_amount = s.sales_dollar_amount + n.sales_dollar_amount
WHEN NOT MATCHED THEN INSERT (sales_transaction_id, sales_quantity_sold,
sales_dollar_amount)
VALUES (n.sales_transcation_id, n.sales_quantity_sold, n.sales_dollar_amount);

ターゲット表に対する無条件のUPDATE ELSE INSERT機能でMERGE文を使用することに加えて、次のことも可能です。

- UPDATEのみ、またはINSERTのみの文を実行する。

- 追加のWHERE条件をMERGE文のUPDATEまたはINSERT部分に適用する。

- 特定の条件がTRUEの場合、UPDATE操作で行を削除する。

例16-2 INSERT句の省略

一部のデータ・ウェアハウス・アプリケーションでは、新しい行を履歴情報に追加することはできず、更新のみが可能なものがあります。また、更新ではなく新しい情報の挿入のみが必要な場合があります。次の文は、UPDATEのみ使用してINSERT機能のみを実行する例です。

MERGE USING Product_Changes S     -- Source/Delta table
INTO Products D1                  -- Destination table 1
ON (D1.PROD_ID = S.PROD_ID)       -- Search/Join condition
WHEN MATCHED THEN UPDATE          -- update if join
SET D1.PROD_STATUS = S.PROD_NEW_STATUS

例16-3 UPDATE句の省略

次の文は、UPDATEを省略する例です。

MERGE USING New_Product S           -- Source/Delta table
INTO Products D2                    -- Destination table 2
ON (D2.PROD_ID = S.PROD_ID)         -- Search/Join condition
WHEN NOT MATCHED THEN               -- insert if no join
INSERT (PROD_ID, PROD_STATUS) VALUES (S.PROD_ID, S.PROD_NEW_STATUS)

INSERT句を省略した場合は、ソース表とターゲット表に対して通常の結合が実行されます。UPDATE句を省略した場合は、ソース表とターゲット表に対してアンチ結合が実行されます。これによって、ソース表とターゲット表がより効率的に結合されます。

例16-4 UPDATE句のスキップ

特定の行を表にマージする際に、UPDATE操作をスキップしなければならない場合があります。この場合、MERGEのUPDATE句にオプションのWHERE句を使用できます。その結果、UPDATE操作は、指定した条件がTRUEの場合にのみ実行されるようになります。次の文は、UPDATE操作をスキップする例を示しています。

MERGE
USING Product_Changes S                      -- Source/Delta table
INTO Products P                              -- Destination table 1
ON (P.PROD_ID = S.PROD_ID)                   -- Search/Join condition
WHEN MATCHED THEN
UPDATE                                       -- update if join
SET P.PROD_LIST_PRICE = S.PROD_NEW_PRICE
WHERE P.PROD_STATUS <> "OBSOLETE"            -- Conditional UPDATE

次に、条件P.PROD_STATUS <> "OBSOLETE"がTRUEではない場合にUPDATE操作がどのようにスキップされるかを示します。条件の述語は、ターゲット表とソース表の両方を指すことができます。

例16-5 MERGE文の条件付き挿入

特定の行を表にマージする際に、INSERT操作をスキップしなければならない場合があります。この場合、MERGEのINSERT句にオプションのWHERE句を追加します。その結果、INSERT操作は、指定した条件がTRUEの場合にのみ実行されるようになります。次に例を示します。

MERGE USING Product_Changes S                      -- Source/Delta table
INTO Products P                                    -- Destination table 1
ON (P.PROD_ID = S.PROD_ID)                         -- Search/Join condition
WHEN MATCHED THEN UPDATE                           -- update if join
SET P.PROD_LIST_PRICE = S.PROD_NEW_PRICE
WHERE P.PROD_STATUS <> "OBSOLETE"                  -- Conditional
WHEN NOT MATCHED THEN
INSERT (PROD_ID, PROD_STATUS, PROD_LIST_PRICE)     -- insert if not join
VALUES (S.PROD_ID, S.PROD_NEW_STATUS, S.PROD_NEW_PRICE)
WHERE S.PROD_STATUS <> "OBSOLETE";                 -- Conditional INSERT

この例は、条件S.PROD_STATUS <> "OBSOLETE"がTRUEではない場合にINSERT操作がスキップされ、条件がTRUEのときにのみINSERTが実行されることを示します。条件の述語は、ソース表のみを指すことができます。条件の述語は、ソース表のみを指すことができます。

例16-6 MERGE文でのDELETE句の使用

表を移入または更新する際に、表をクレンジングする場合があります。この場合、次の例のように、MERGE文でDELETE句を使用できます。

MERGE USING Product_Changes S
INTO Products D ON (D.PROD_ID = S.PROD_ID)
WHEN MATCHED THEN
UPDATE SET D.PROD_LIST_PRICE =S.PROD_NEW_PRICE, D.PROD_STATUS = S.PROD_NEWSTATUS
DELETE WHERE (D.PROD_STATUS = "OBSOLETE")
WHEN NOT MATCHED THEN
INSERT (PROD_ID, PROD_LIST_PRICE, PROD_STATUS)
VALUES (S.PROD_ID, S.PROD_NEW_PRICE, S.PROD_NEW_STATUS);

このように、行がproductsで更新される際に、削除条件D.PROD_STATUS = "OBSOLETE"がチェックされ、条件がTRUEであれば行が削除されます。

DELETE操作は、完全なDELETE文の削除とは異なります。MERGE先の行のみが削除されます。DELETEの影響を受ける行のみが、このMERGE文で更新される行です。したがって、目的の表で指定した行が削除条件に合致しても、ON句の条件の下で結合を実行しない場合は削除されません。

例16-7 MERGE文の無条件の挿入

ソースのすべての行を表に挿入する場合があります。この場合、ソース表とターゲット表の結合を避けることができます。1=0など、常にFALSEになる特殊な一定の結合条件を指定することで、このようなMERGE文は最適化され、結合条件は無効になります。

MERGE USING New_Product S       -- Source/Delta table
INTO Products P                 -- Destination table 1
ON (1 = 0)                      -- Search/Join condition
WHEN NOT MATCHED THEN           -- insert if no join
INSERT (PROD_ID, PROD_STATUS) VALUES (S.PROD_ID, S.PROD_NEW_STATUS)

参照整合性の保持

データ・ウェアハウス環境によっては、参照整合性を保証するために新しいデータを表に挿入する必要がある場合もあります。たとえば、キャッシュ・レジスタから直接データを取り出す業務系システムからsalesを導出するデータ・ウェアハウスがあるとします。salesは毎晩リフレッシュされます。ただし、productディメンション表のデータは別の業務系システムから導出されます。productディメンション表の変更には比較的時間がかかるため、この表は週に1回しかリフレッシュされないことがあります。新製品が月曜日に導入されたとすると、その製品のproduct_idがデータ・ウェアハウスのproduct表に挿入される前に、データ・ウェアハウスのsalesデータ内にproduct_idが表示される可能性があります。

この新製品の売上トランザクションは有効ですが、その売上データは、productディメンション表とsalesファクト表間の参照整合性制約を満たしません。この場合、新しい売上トランザクションを禁止するより、sales表にその売上トランザクションを挿入する方を選ぶのが普通です。ただし、sales表とproduct表間の参照整合性関係もメンテナンスする必要があります。これは、新しい行を不明な製品のプレースホルダとしてproduct表に挿入することによって可能になります。

前述の例のように、sales表への新しいデータは別のnew_sales表に格納されるとします。パラレル化が可能な単一のINSERT文で、product表が新製品を反映するように変更できます。

INSERT INTO product
(SELECT sales_product_id, 'Unknown Product Name', NULL, NULL ...
FROM new_sales WHERE sales_product_id NOT IN
(SELECT product_id FROM product));

データの削除

データ・ウェアハウスから大量のデータを削除する必要がある場合もあります。前述のローリング・ウィンドウでは、古いデータをデータ・ウェアハウスからロールアウトして新しいデータの領域を確保するという、非常に一般的な例を取り上げました。

ただし、それ以外の場合でも、データをデータ・ウェアハウスから削除する必要がある場合があります。たとえば、ある小売会社が、以前にXYZ Software社の製品を販売し、その後XYZ Softwareが廃業したとします。データ・ウェアハウスを業務で利用しているユーザーが、XYZ Software社に関するデータはもう必要ないと判断し、このデータを削除することになりました。

大量のデータを削除する方法の1つに、パラレル削除による方法があります。例を次に示します。

DELETE FROM sales WHERE sales_product_id IN (SELECT product_id
FROM product WHERE product_category = 'XYZ Software');

このSQL文では、パーティションごとに1つのパラレル処理が起動されます。この方法のメリットは、シリアルDELETE文よりはるかに効率的で、sales表のデータは移動する必要がないことです。ただし、いくつかのデメリットもあります。行の大部分を削除する場合、DELETE文は既存のパーティションに多数の行スロットを残します。その後、新しいデータがローリング・ウィンドウ・テクニックによってロードされても(またはダイレクト・パス・インサートまたはダイレクト・パス・ロードによってロードされても)、この記憶領域は再使用されません。また、DELETE文はパラレル化できますが、それより効率的な方法もあります。別の方法としては、XYZ Software社以外の製品カテゴリのデータをすべて維持したまま、sales表全体を再作成する方法があります。

CREATE TABLE sales2 AS SELECT * FROM sales, product
WHERE sales.sales_product_id = product.product_id
AND product_category <> 'XYZ Software'
NOLOGGING PARALLEL (DEGREE 8)
#PARTITION ... ; #create indexes, constraints, and so on
DROP TABLE SALES;
RENAME SALES2 TO SALES;

この方法は、パラレルDELETEより効率的です。ただし、sales表を2回インスタンス化することになるため、この方法もディスク領域の使用量の面ではコストが高くなります。

ディスク領域の使用量が少ない別の方法として、sales表を一度に1パーティションずつ再作成する方法があります。

CREATE TABLE sales_temp AS SELECT * FROM sales WHERE 1=0;
INSERT INTO sales_temp
SELECT * FROM sales PARTITION (sales_99jan), product
WHERE sales.sales_product_id = product.product_id
AND product_category <> 'XYZ Software';
<create appropriate indexes and constraints on sales_temp>
ALTER TABLE sales EXCHANGE PARTITION sales_99jan WITH TABLE sales_temp;

sales表の各パーティションに対して、このプロセスを繰り返します。

マテリアライズド・ビューのリフレッシュ

マテリアライズド・ビューを作成する場合、リフレッシュをON DEMANDで行うか、ON COMMITで行うかを指定できます。ON COMMITの場合は、トランザクションがコミットされるたびに、マテリアライズド・ビューが変更されます。このため、マテリアライズド・ビューに常に最新データが含まれていることが保証されます。または、ON DEMANDを指定すると、マテリアライズド・ビューのリフレッシュが発生する時期を制御できます。この場合、マテリアライズド・ビューをリフレッシュするには、DBMS_MVIEWパッケージ内のプロシージャの1つをコールする必要があります。

DBMS_MVIEWパッケージでは、次の3通りのリフレッシュ操作が利用できます。

- DBMS_MVIEW.REFRESH
1つまたは複数のマテリアライズド・ビューをリフレッシュします。

- DBMS_MVIEW.REFRESH_ALL_MVIEWS
すべてのマテリアライズド・ビューをリフレッシュします。

- DBMS_MVIEW.REFRESH_DEPENDENT
特定のマスター表またはマテリアライズド・ビュー、あるいはマスター表やマテリアライズド・ビューのリストに依存するマテリアライズド・ビューをすべてリフレッシュします。

参照:
詳細は、「DBMS_MVIEWパッケージによる手動リフレッシュ」を参照してください

リフレッシュ操作の実行には、索引再構築のための一時領域が必要で、リフレッシュ操作そのものを実行するために追加の領域が必要な場合もあります。サイトによっては、マテリアライズド・ビューを同時にすべてリフレッシュすることが適切ではない場合もあります。ベースとなるディテール・データが更新されると、このデータを使用するすべてのマテリアライズド・ビューが失効します。したがって、マテリアライズド・ビューのリフレッシュを遅延させる場合は、選択したリライトの整合性レベルを信頼して、失効したマテリアライズド・ビューがクエリー・リライトに使用できるかどうかを判断するか、または、ALTER SYSTEM SET QUERY_REWRITE_ENABLED = FALSE文でクエリー・リライトを一時的に使用禁止にできます。マテリアライズド・ビューのリフレッシュ後に、ALTER SYSTEM SET QUERY_REWRITE_ENABLEDにTRUEを指定すると、現行のデータベース・インスタンスにあるすべてのセッションに対して、クエリー・リライトをデフォルトの状態である使用可能に戻すことができます。マテリアライズド・ビューをリフレッシュすると、そのすべての索引が自動的に更新されます。完全リフレッシュの場合は、リフレッシュ中にすべての索引を再作成できるように、一時ソート領域が必要です。これは、完全リフレッシュでは、新しいデータ・ボリューム全体が挿入される前に表が切り捨てられるか、削除されるためです。索引再作成のための一時領域が不足している場合は、リフレッシュ操作の前に明示的に各索引を削除するか、UNUSABLEマークを付ける必要があります。

マテリアライズド・ビューで参照される表に対する挿入、更新または削除操作が、そのマテリアライズド・ビューのリフレッシュと同時に実行されると予想され、そのマテリアライズド・ビューに結合と集計が含まれている場合は、ON DEMAND高速リフレッシュではなくON COMMIT高速リフレッシュを使用することをお薦めします。

関連項目:
キューブ・マテリアライズド・ビューのリフレッシュの詳細は、『Oracle OLAPユーザーズ・ガイド』を参照してください。

完全リフレッシュ

完全リフレッシュは、マテリアライズド・ビューを最初にBUILD IMMEDIATEで定義した場合に実行されます。ただし、そのマテリアライズド・ビューが事前作成表を参照する場合は除きます。BUILD DEFERREDを使用するマテリアライズド・ビューの場合は、初めて使用する前に完全リフレッシュを実行する必要があります。完全リフレッシュは、どのマテリアライズド・ビューの場合も必要に応じて要求できます。このリフレッシュでは、ディテール表が読み込まれ、マテリアライズド・ビューの結果が計算されます。これは、読み込まれて処理されるデータが大量の場合には、時間がかかる処理です。そのため、完全リフレッシュを要求する前に、必ずその処理時間を考慮してください。

また、すでに作成済のマテリアライズド・ビューが次の項で説明する高速リフレッシュの条件を満たしていないために、完全リフレッシュしか使用できない場合もあります。

高速リフレッシュ

ほとんどのデータ・ウェアハウスでは、そのディテール・データが定期的に増分更新されます。「マテリアライズド・ビューのスキーマ・デザイン」で説明したように、SQL*Loaderまたはバルク・ロード・ユーティリティを使用して、ディテール・データの増分ロードを実行できます。通常、マテリアライズド・ビューの高速リフレッシュは効率的です。これは、すべてのマテリアライズド・ビューを再計算するのではなく、変更分のみが既存のデータに適用されるためです。このように、変更のみを処理するため、リフレッシュ時間を大幅に削減できます。

パーティション・チェンジ・トラッキング(PCT)リフレッシュ

ディテール表に対するパーティション・メンテナンス操作がある場合、高速リフレッシュにはこの方法しか使用できません。マテリアライズド・ビューに対するPCTベースのリフレッシュは、「パーティション・チェンジ・トラッキング」で説明しているすべての条件を満たした場合にのみ有効です。

ディテール表に対するパーティション・メンテナンス操作がない場合、DBMS_MVIEWパッケージのプロシージャでリフレッシュのFASTメソッド(method => 'F')を要求すると、PCTリフレッシュが選択される前に、ログベースのルールの高速リフレッシュを試行するヒューリスティックなルールが使用されます。同様に、FORCEメソッド(method => '?')を要求すると、ログベースの高速リフレッシュ、PCTリフレッシュ、完全リフレッシュの試行順序に基づいてリフレッシュ方法が選択されます。または、PCTメソッド(method => 'P')を要求することもできます。Oracleは、すべてのPCT要件が満たされている場合はPCTメソッドを使用します。

「マテリアライズド・ビューのパーティション化によるメリット」で説明されている条件を満たしており、PCTリフレッシュ・プロセスを効率的に実行できる場合は、マテリアライズド・ビューに対してTRUNCATE PARTITIONを使用できます。

ON COMMITリフレッシュ

ON COMMITメソッドを使用すると、マテリアライズド・ビューを自動的にリフレッシュできます。したがって、マテリアライズド・ビューが定義されている表の更新トランザクションがコミットされるたびに、その変更内容がマテリアライズド・ビューに自動的に反映されます。このアプローチを使用するメリットは、マテリアライズド・ビューのリフレッシュに注意する必要がないことです。唯一のデメリットは、余分な処理が必要なため、コミット完了までの所要時間が少し長くなることです。ただし、データ・ウェアハウスでは、同時プロセスで同じ表の更新が試みられることはまずないため、これは問題ではありません。

DBMS_MVIEWパッケージによる手動リフレッシュ

マテリアライズド・ビューのON DEMANDリフレッシュを行う場合は、次の表のように4つのリフレッシュ方法から1つ指定できます。デフォルト・オプションは、マテリアライズド・ビューの作成時に定義できます。表16-1にリフレッシュ・オプションを示します。

表16-1 ON DEMANDリフレッシュ方法

リフレッシュ・オプション

パラメータ

説明

COMPLETE

C

マテリアライズド・ビューの定義問合せを再計算することでリフレッシュします。

FAST

F

マテリアライズド・ビューへの変更を増分的に適用することでリフレッシュします。

ローカル・マテリアライズド・ビューの場合は、オプティマイザが最も効率的と判断したリフレッシュ方法が選択されます。考えられるリフレッシュ方法は、ログベースのFASTとFAST_PCTです。

FAST_PCT

P

ディテール表で変更されたパーティションの影響を受けるマテリアライズド・ビューの行を再計算することでリフレッシュします。

FORCE

?

高速リフレッシュを試みます。それができない場合は、完全リフレッシュを行います。

ローカル・マテリアライズド・ビューの場合は、オプティマイザが最も効率的と判断したリフレッシュ方法が選択されます。考えられるリフレッシュ方法は、ログベースのFAST、FAST_PCTおよびCOMPLETEです。

ON DEMANDリフレッシュを実行するために、DBMS_MVIEWパッケージの3つのリフレッシュ・プロシージャを使用できます。それぞれに、一連の固有のパラメータがあります。

関連項目:

- DBMS_MVIEWパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

- レプリケーション環境で使用する方法は、『Oracle Databaseアドバンスト・レプリケーション』を参照してください。

REFRESHを使用した特定のマテリアライズド・ビューのリフレッシュ

DBMS_MVIEW.REFRESHプロシージャを使用して、1つ以上のマテリアライズド・ビューをリフレッシュできます。一部のパラメータはレプリケーションにのみ使用されるため、ここでは説明しません。このプロシージャを使用するために必要なパラメータは次のとおりです。

- カンマで区切られた、リフレッシュ対象のマテリアライズド・ビューのリスト

- リフレッシュ方法: F-Fast、P-Fast_PCT、?-Force、C-Complete

- 使用するロールバック・セグメント

- エラー発生後のリフレッシュ(TRUEまたはFALSE)
ブール・パラメータ。TRUEに設定すると、number_of_failures出力パラメータは失敗したリフレッシュの数に設定され、通常のエラー・メッセージによって失敗の発生が示されます。インスタンスに関するアラート・ログに、リフレッシュ・エラーの詳細が示されます。デフォルト値のFALSEに設定すると、最初にエラーが発生したときにリフレッシュが停止し、リスト内の残りのマテリアライズド・ビューはリフレッシュされません。

- 次の4つのパラメータは、レプリケーション・プロセスで使用されます。ウェアハウスのリフレッシュの場合は、FALSE, 0,0,0に設定してください。

- アトミック・リフレッシュ(TRUEまたはFALSE)
TRUEに設定すると、すべてのリフレッシュが1トランザクションで行われます。FALSEに設定すると、指定したマテリアライズド・ビューのリフレッシュがそれぞれ別のトランザクションで行われます。FALSEに設定すると、パラレルDMLによってリフレッシュが最適化され、マテリアライズド・ビューのDDLが切り捨てられます。マテリアライズド・ビューをアトミック・モードでリフレッシュする際、リライト整合性モードがstale_toleratedに設定されている場合にクエリー・リライトを使用できます。リフレッシュがネステッド・ビューで実行される場合は、アトミック・リフレッシュは保証されません。

たとえば、マテリアライズド・ビューcal_month_sales_mvに高速リフレッシュを行うには、DBMS_MVIEWパッケージを次のようにコールします。

DBMS_MVIEW.REFRESH('CAL_MONTH_SALES_MV', 'F', '', TRUE, FALSE, 0,0,0, FALSE);

複数のマテリアライズド・ビューを同時にリフレッシュすることが可能です。また、その際、すべてに同じリフレッシュ方法を使用する必要はありません。それぞれに異なるリフレッシュ方法を適用するには、マテリアライズド・ビューのリスト順に複数のメソッド・コードを指定します(カンマなし)。たとえば、次のように指定すると、cal_month_sales_mvは完全リフレッシュされ、fweek_pscat_sales_mvは高速リフレッシュされます。

DBMS_MVIEW.REFRESH('CAL_MONTH_SALES_MV, FWEEK_PSCAT_SALES_MV', 'CF', '',
TRUE, FALSE, 0,0,0, FALSE);

リフレッシュ方法を指定しなければ、マテリアライズド・ビューの定義で指定したデフォルトのリフレッシュ方法が使用されます。

REFRESH_ALL_MVIEWSを使用したすべてのマテリアライズド・ビューのリフレッシュ

リフレッシュするマテリアライズド・ビューを指定する場合、プロシージャDBMS_MVIEW.REFRESH_ALL_MVIEWSを使用する方法もあります。このプロシージャでは、すべてのマテリアライズド・ビューがリフレッシュされます。リフレッシュに失敗したマテリアライズド・ビューがあると、その数がレポートされます。

このプロシージャのパラメータは次のとおりです。

- 失敗数(OUT変数)

- リフレッシュ方法: F-Fast、P-Fast_PCT、?-Force、C-Complete

- エラー発生後のリフレッシュ(TRUEまたはFALSE)
ブール・パラメータ。TRUEに設定すると、number_of_failures出力パラメータは失敗したリフレッシュの数に設定され、通常のエラー・メッセージによって失敗の発生が示されます。インスタンスに関するアラート・ログに、リフレッシュ・エラーの詳細が示されます。デフォルト値のFALSEに設定すると、最初にエラーが発生したときにリフレッシュが停止し、リスト内の残りのマテリアライズド・ビューはリフレッシュされません。

- アトミック・リフレッシュ(TRUEまたはFALSE)
TRUEに設定すると、すべてのリフレッシュが1トランザクションで行われます。FALSEに設定されると、各マテリアライズド・ビューは別々のトランザクションで非アトミックにリフレッシュされます。FALSEに設定すると、パラレルDMLによってリフレッシュが最適化され、マテリアライズド・ビューのDDLが切り捨てられます。マテリアライズド・ビューをアトミック・モードでリフレッシュする際、リライト整合性モードがstale_toleratedに設定されている場合にクエリー・リライトを使用できます。リフレッシュがネステッド・ビューで実行される場合は、アトミック・リフレッシュは保証されません。

次に、すべてのマテリアライズド・ビューをリフレッシュする例を示します。

DBMS_MVIEW.REFRESH_ALL_MVIEWS(failures,'C','', TRUE, FALSE);

REFRESH_DEPENDENTを使用した依存マテリアライズド・ビューのリフレッシュ

3番目のプロシージャDBMS_MVIEW.REFRESH_DEPENDENTでは、特定の表または表のリストに依存するマテリアライズド・ビューのみがリフレッシュされます。たとえば、変更がorders表には反映されるが、customer payments表には反映されないとします。orders表を参照するマテリアライズド・ビューのみをリフレッシュするには、REFRESH_DEPENDENTプロシージャをコールします。

このプロシージャのパラメータは次のとおりです。

- 失敗数(OUT変数)

- 依存する表

- リフレッシュ方法: F-Fast、P-Fast_PCT、?-Force、C-Complete

- 使用するロールバック・セグメント

- エラー発生後のリフレッシュ(TRUEまたはFALSE)
ブール・パラメータ。TRUEに設定すると、number_of_failures出力パラメータは失敗したリフレッシュの数に設定され、通常のエラー・メッセージによって失敗の発生が示されます。インスタンスに関するアラート・ログに、リフレッシュ・エラーの詳細が示されます。デフォルト値のFALSEに設定すると、最初にエラーが発生したときにリフレッシュが停止し、リスト内の残りのマテリアライズド・ビューはリフレッシュされません。

- アトミック・リフレッシュ(TRUEまたはFALSE)
TRUEに設定すると、すべてのリフレッシュが1トランザクションで行われます。FALSEに設定すると、指定したマテリアライズド・ビューのリフレッシュがそれぞれ別のトランザクションで行われます。FALSEに設定すると、パラレルDMLによってリフレッシュが最適化され、マテリアライズド・ビューのDDLが切り捨てられます。マテリアライズド・ビューをアトミック・モードでリフレッシュする際、リライト整合性モードがstale_toleratedに設定されている場合にクエリー・リライトを使用できます。リフレッシュがネステッド・ビューで実行される場合は、アトミック・リフレッシュは保証されません。

- ネストされているかどうか
TRUEに設定すると、依存順序に基づいて指定した表のセットのすべての依存マテリアライズド・ビューがリフレッシュされ、マテリアライズド・ビューが実表に対して最新の状態になります。

customers表を参照するすべてのマテリアライズド・ビューの完全リフレッシュを実行するには、次のように指定します。

DBMS_MVIEW.REFRESH_DEPENDENT(failures, 'CUSTOMERS', 'C', '', FALSE, FALSE );

リフレッシュへのジョブ・キューの使用

ジョブ・キューを使用して、複数のマテリアライズド・ビューをパラレルにリフレッシュできます。キューが使用できない場合、高速リフレッシュでは各ビューがフォアグラウンド・プロセスで順次リフレッシュされます。キューを使用可能にするには、JOB_QUEUE_PROCESSESパラメータを設定する必要があります。このパラメータでバックグラウンド・ジョブ・キュー・プロセスの数を定義することにより、同時に実行可能なマテリアライズド・ビューの数が決まります。同時リフレッシュの数と各リフレッシュの並列度のバランスが調整されます。マテリアライズド・ビューがリフレッシュされる順序は、ネステッド・マテリアライズド・ビューで指定されている依存性、および他のマテリアライズド・ビューに対するクエリー・リライトによる効率的なリフレッシュの可能性によって決まります(詳細は、「リフレッシュのスケジューリング」を参照)。このパラメータが有効なのは、atomic_refreshがFALSEに設定されている場合のみです。

DBMS_MVIEW.REFRESHを実行中のプロセスに割込みが入るか、そのインスタンスが停止すると、ジョブ・キュー・プロセスで実行中だったリフレッシュ・ジョブが再度キューに入れられ、引き続き実行されます。これらのジョブを削除するには、DBMS_JOB.REMOVEプロシージャを使用します。

高速リフレッシュが可能なパターン

すべてのマテリアライズド・ビューの高速リフレッシュが可能であるとは限りません。したがって、パッケージDBMS_MVIEW.EXPLAIN_MVIEWを使用して、マテリアライズド・ビューに使用可能なリフレッシュ方法を判断します。DBMS_MVIEWパッケージの詳細は、第9章「基本的なマテリアライズド・ビュー」を参照してください。

マテリアライズド・ビューを高速リフレッシュできるようにする方法がわからない場合は、DBMS_ADVISOR.TUNE_MVIEWプロシージャを使用して、高速リフレッシュできるマテリアライズド・ビューの作成に必要な文を含むスクリプトを指定します。詳細は、『Oracle Databaseパフォーマンス・チューニング・ガイド』を参照してください。

パラレル化の推奨初期化パラメータ

パラレル化を効果的にするには、次の初期化パラメータを正しく設定する必要があります。

- PARALLEL_MAX_SERVERSは、パラレル化に対応できるように十分高い値にします。リフレッシュ文に必要なスレーブの数を考慮する必要があります。たとえば、並列度が8であれば、16のスレーブ・プロセスが必要です。

- インスタンスでソートと結合のメモリー使用を自動的に管理するには、PGA_AGGREGATE_TARGETを設定する必要があります。メモリー・パラメータを手動で設定する場合は、SORT_AREA_SIZEをHASH_AREA_SIZE未満にする必要があります。

- OPTIMIZER_MODEは、all_rowsと同じ値にします。

すべての表および索引を効率的に分析すると、最適化が向上します。

関連項目:
詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。

リフレッシュの監視

ジョブの実行中は、V$SESSION_LONGOPSビューを問い合せて、各マテリアライズド・ビューのリフレッシュの進行状況を確認できます。

SELECT * FROM V$SESSION_LONGOPS;

どのジョブがどのキューに入っているかを確認するには、次の文を使用します。

SELECT * FROM DBA_JOBS_RUNNING;

マテリアライズド・ビューのステータスのチェック

マテリアライズド・ビューのステータスをチェックできるように、DBA_MVEIWS、ALL_MVIEWS、USER_MVIEWSの3つのビューが用意されています。マテリアライズド・ビューが最新か失効しているかをチェックするには、次の文を発行します。

SELECT MVIEW_NAME, STALENESS, LAST_REFRESH_TYPE, COMPILE_STATE
FROM USER_MVIEWS ORDER BY MVIEW_NAME;

MVIEW_NAME            STALENESS      LAST_REF       COMPILE_STATE
----------            ---------      --------       -------------
CUST_MTH_SALES_MV     NEEDS_COMPILE  FAST           NEEDS_COMPILE
PROD_YR_SALES_MV      FRESH          FAST           VALID

compile_state列がNEEDS COMPILEとなっている場合、表示されているその他の列の値は信頼できず、実際のステータスが反映されていない場合があります。マテリアライズド・ビューを再検証するには、次の文を発行します。

ALTER MATERIALIZED VIEW [materialized_view_name] COMPILE;

続いて、SELECT文を再度発行します。

パーティションの最新状態の表示

一部のビューでは、実表のパーティションの状態を確認し、マテリアライズド・ビューのデータの最新および失効の範囲を判別できます。該当のビューは次のとおりです。

- *_USER_MVIEWS
マテリアライズド・ビューのパーティション・チェンジ・トラッキング(PCT)情報の判別に使用します。

- *_USER_MVIEW_DETAIL_RELATIONS
マテリアライズド・ビューの基になるディテール表のパーティション情報を表示するのに使用します。

- *_USER_MVIEW_DETAIL_PARTITION
最新のパーティションを判別するのに使用します。

- *_USER_MVIEW_DETAIL_SUBPARTITION
最新のサブパーティションを判別するのに使用します。

これらのビューの使用例を次に示します。図16-1は、レンジ-リスト・パーティション化された表とそれに基づくマテリアライズド・ビューを示しています。P1、P2、P3はパーティションで、SP1、SP2、SP3はサブパーティションです。

図16-1 PCTによる最新状態の判別

最新状態の判別に使用するビューの使用例

この項では、マテリアライズド・ビューとそのディテール表のPCTおよび最新状態の情報を判別するための例を示します。

例16-8 マテリアライズド・ビューのPCTステータスの確認

マテリアライズド・ビューのPCT情報にアクセスするには、次のようにUSER_MVIEWSを問い合せます。

SELECT MVIEW_NAME, NUM_PCT_TABLES, NUM_FRESH_PCT_REGIONS,
NUM_STALE_PCT_REGIONS
FROM USER_MVIEWS
WHERE MVIEW_NAME = MV1;

MVIEW_NAME NUM_PCT_TABLES NUM_FRESH_PCT_REGIONS NUM_STALE_PCT_REGIONS
---------- -------------- --------------------- ---------------------
MV1              1                     9                     3

例16-9 マテリアライズド・ビューのディテール表におけるPCTステータスの確認

PCTディテール表の情報にアクセスするには、次のようにUSER_MVIEW_DETAIL_RELATIONSを問い合せます。

SELECT MVIEW_NAME, DETAILOBJ_NAME, DETAILOBJ_PCT,
NUM_FRESH_PCT_PARTITIONS, NUM_STALE_PCT_PARTITIONS
FROM USER_MVIEW_DETAIL_RELATIONS
WHERE MVIEW_NAME = MV1;

MVIEW_NAME  DETAILOBJ_NAME  DETAIL_OBJ_PCT  NUM_FRESH_PCT_PARTITIONS  NUM_STALE_PCT_PARTITIONS
----------  --------------  --------------  ------------------------  ------------------------
MV1             T1               Y                         3                         1

例16-10 最新のパーティションの確認

パーティションのPCTの最新状態の情報にアクセスするには、次のようにUSER_MVIEW_DETAIL_PARTITIONを問い合せます。

SELECT MVIEW_NAME,DETAILOBJ_NAME,DETAIL_PARTITION_NAME,
DETAIL_PARTITION_POSITION,FRESHNESS
FROM USER_MVIEW_DETAIL_PARTITION
WHERE MVIEW_NAME = MV1;

MVIEW_NAME  DETAILOBJ_NAME  DETAIL_PARTITION_NAME  DETAIL_PARTITION_POSITION  FRESHNESS
----------  --------------  ---------------------  -------------------------  ---------
MV1               T1                    P1                          1      FRESH
MV1               T1                    P2                          2      FRESH
MV1               T1                    P3                          3      STALE
MV1               T1                    P4                          4      FRESH

例16-11 最新のサブパーティションの確認

サブパーティションのPCTの最新状態の情報にアクセスするには、次のようにUSER_MVIEW_DETAIL_SUBPARTITIONを問い合せます。

SELECT MVIEW_NAME,DETAILOBJ_NAME,DETAIL_PARTITION_NAME, DETAIL_SUBPARTITION_NAME,
DETAIL_SUBPARTITION_POSITION,FRESHNESS
FROM USER_MVIEW_DETAIL_SUBPARTITION
WHERE MVIEW_NAME = MV1;

MVIEW_NAME DETAILOBJ DETAIL_PARTITION DETAIL_SUBPARTITION_NAME DETAIL_SUBPARTITION_POS FRESHNESS
---------- --------- ---------------- ------------------------ ----------------------- ---------
MV1        T1               P1                      SP1                       1     FRESH
MV1        T1               P1                      SP2                       1     FRESH
MV1        T1               P1                      SP3                       1     FRESH
MV1        T1               P2                      SP1                       1     FRESH
MV1        T1               P2                      SP2                       1     FRESH
MV1        T1               P2                      SP3                       1     FRESH
MV1        T1               P3                      SP1                       1     STALE
MV1        T1               P3                      SP2                       1     STALE
MV1        T1               P3                      SP3                       1     STALE
MV1        T1               P4                      SP1                       1     FRESH
MV1        T1               P4                      SP2                       1     FRESH
MV1        T1               P4                      SP3                       1     FRESH

リフレッシュのスケジューリング

ほとんどの場合、データベースには複数のマテリアライズド・ビューがあります。一部のマテリアライズド・ビューは、他のマテリアライズド・ビューに対してリライトして計算されているものもあります。これは、ネステッド・マテリアライズド・ビューがある、または一部の階層の異なるレベルにマテリアライズド・ビューがあるデータ・ウェアハウス環境では、非常に一般的なことです。

このような場合、BUILD DEFERREDとしてマテリアライズド・ビューを作成し、DBMS_MVIEWパッケージのいずれかのリフレッシュ・プロシージャを発行してすべてのマテリアライズド・ビューをリフレッシュする必要があります。Oracle Databaseによって、依存性が計算され、正しい順序でマテリアライズド・ビューがリフレッシュされます。「階層的キューブのマテリアライズド・ビューの例」で説明されている完全な階層的キューブの例を参照してください。すべてのマテリアライズド・ビューがBUILD DEFERREDとして作成されているとします。マテリアライズド・ビューをBUILD DEFERREDとして作成すると、すべてのマテリアライズド・ビューのメタデータのみが作成されます。これで、DBMS_MVIEWパッケージのリフレッシュ・プロシージャのいずれかをコールし、すべてのマテリアライズド・ビューを正しい順序でリフレッシュできます。

DECLARE numerrs PLS_INTEGER;
BEGIN DBMS_MVIEW.REFRESH_DEPENDENT (
number_of_failures => numerrs, list=>'SALES', method => 'C');
DBMS_OUTPUT.PUT_LINE('There were ' || numerrs || ' errors during refresh');
END;
/

プロシージャは、マテリアライズド・ビューを依存性の順序(sales_hierarchical_mon_cube_mvから始まり、sales_hierarchical_qtr_cube_mv、sales_hierarchical_yr_cube_mv、sales_hierarchical_all_cube_mvという順序)でリフレッシュします。各マテリアライズド・ビューは、リストにおける1つ前のマテリアライズド・ビューに対してリライトされます。

同じ種類のリライトは、PCTリフレッシュを行う際にも使用できます。PCTリフレッシュでは、ディテール表で変更された行に対応するマテリアライズド・ビューの行が再計算されます。リフレッシュ時に最新のマテリアライズド・ビューが他にある場合は、ディテール表ではなくそのマテリアライズド・ビューが直接使用されます。

したがって、指定した方法に関係なく、マテリアライズド・ビューのリストをDBMS_MVIEWパッケージのいずれかのリフレッシュ・プロシージャに渡すことは有益です。これによって、プロシージャは、マテリアライズド・ビューに対してリフレッシュを実行する順序を認識できます。

集計を含むマテリアライズド・ビューのリフレッシュのヒント

ここでは、集計を含むマテリアライズド・ビューのリフレッシュ機能を使用する場合のガイドラインを示します。

- 高速リフレッシュの場合は、ROWID、SEQUENCEおよびINCLUDING NEW VALUES句を使用して、マテリアライズド・ビューに関連するすべてのディテール表のマテリアライズド・ビュー・ログを作成します。
マテリアライズド・ビュー・ログには、マテリアライズド・ビューに使用されると思われる表の列をすべて含めます。

高速リフレッシュは、マテリアライズド・ビュー・ログでSEQUENCEオプションが省略されていても可能な場合があります。すべてのディテール表で挿入または削除しか発生しないと判断できる場合、マテリアライズド・ビュー・ログにはSEQUENCE句は不要です。ただし、複数の表が更新される可能性があるか必要な場合、または特定の更新の手順が不明な場合は、SEQUENCE句が含まれているかどうかを確認してください。

- Oracleのバルク・ロード・ユーティリティまたはダイレクト・パス・インサート(ロードに対するAPPENDヒント付きのINSERT)を使用します。
これは、従来の挿入に比べてはるかに効率的です。ロード中はすべての制約を使用禁止にし、ロード終了後に使用可能に戻します。ダイレクト・ロードと従来型DMLのどちらを使用する場合も、マテリアライズド・ビュー・ログは必要であるため注意してください。

マテリアライズド・ビューに対する従来型の複合DML操作、ダイレクト・パス・インサートおよび高速リフレッシュの順序を最適化してください。従来のDMLおよびダイレクト・ロードと混在させて高速リフレッシュを使用できます。高速リフレッシュでは、次に示すように、ダイレクト・ロードのみが発生することがわかっていれば、大幅な最適化を実行できます。

1. ディテール表へのダイレクト・パス・インサート(SQL*LoaderまたはINSERT /*+ APPEND */)を行います。

2. マテリアライズド・ビューのリフレッシュ

3. 従来型の複合DML操作を行います。

4. マテリアライズド・ビューのリフレッシュ

高速リフレッシュを、ディテール表に対する従来型の複合DML(INSERT、UPDATEおよびDELETE)と併用できます。ただし、高速リフレッシュで処理中に大幅な最適化を実行できるのは、次のように、表に対して挿入または削除のみが行われていることが検出される場合です。

- ディテール表に対するDML INSERTまたはDELETE

- 複数のマテリアライズド・ビューのリフレッシュ

- ディテール表のDML更新

- マテリアライズド・ビューのリフレッシュ

さらに最適化するには、INSERTとDELETEを分離します。

可能であれば、最後にリフレッシュを1つのみ発行するのではなく、前述のように各種のデータ変更の後にリフレッシュを実行します。可能でない場合は、従来のDMLを挿入対象の表に限定すると、リフレッシュのパフォーマンスが大幅に改善されます。DELETEとダイレクト・ロードの混在を回避してください。

さらに、ON COMMITリフレッシュの場合、Oracleは、コミットされたトランザクションで実行されたDMLのタイプを記録します。したがって、同じトランザクションの他の表には、ダイレクト・パス・インサートおよびDMLを実行しないでください。Oracleがリフレッシュ・フェーズを最適化できない可能性があります。

ON COMMITマテリアライズド・ビューの場合は、各トランザクションの最後にリフレッシュが自動的に発生します。DML文を分離できない場合があり、その場合はトランザクションを短くすると有効です。ただし、ディテール表に対して多数の変更を行う場合は、更新のたびにリフレッシュするより、それらの更新を1回のトランザクションで実行し、コミット時に一度にマテリアライズド・ビューをリフレッシュする方が効率的です。

- 次の方法を使用できるため、表をパーティション化することをお薦めします。

- パラレルDML
大量のロードまたはリフレッシュの場合は、パラレルDMLを使用可能にすると、処理時間を短縮できます。

- パーティション・チェンジ・トラッキング(PCT)高速リフレッシュ
マテリアライズド・ビューの高速リフレッシュは、ディテール表に対するパーティション・メンテナンス操作後に実行できます。マテリアライズド・ビューにPCTを使用可能にする方法の詳細は、「パーティション・チェンジ・トラッキング」を参照してください。

- また、マテリアライズド・ビューをパーティション化すると、リフレッシュでパラレルDMLを使用してマテリアライズド・ビューを更新できるため、パフォーマンスが改善されます。たとえば、ディテール表とマテリアライズド・ビューがパーティション化されており、PARALLEL句があるとします。次の順序により、Oracleではマテリアライズド・ビューのリフレッシュをパラレル化できます。

1. ディテール表にバルク・ロードします。

2. ALTER SESSION ENABLE PARALLEL DML文でパラレルDMLを使用可能にします。

3. マテリアライズド・ビューをリフレッシュします。

- DBMS_MVIEW.REFRESHを使用してリフレッシュを行うには、パラメータatomic_refreshをFALSEに設定します。

- COMPLETEリフレッシュでは、これにより、マテリアライズド・ビューの既存の行がTRUNCATEによって削除されます。これはDELETEより高速です。

- PCTリフレッシュでは、マテリアライズド・ビューが適切にパーティション化されている場合は、これにより、マテリアライズド・ビューの影響を受けるパーティションの行がTRUNCATE PARTITIONによって削除されます。これはDELETEより高速です。

- FASTまたはFORCEリフレッシュでは、COMPLETEリフレッシュまたはPCTリフレッシュが選択された場合、これにより、前述のTRUNCATE最適化を使用できます。

- JOB_QUEUESを指定してDBMS_MVIEW.REFRESHを使用する場合は、atomicをFALSEに設定してください。このように設定しなければ、JOB_QUEUESは使用されません。ジョブ・キュー・プロセスの数を、プロセッサ数より大きくなるように設定します。
ジョブ・キューが使用可能になっており、リフレッシュするマテリアライズド・ビューが多い場合は、個別にコールするより1つのコマンドですべてをリフレッシュするほうが高速です。

- リフレッシュしたマテリアライズド・ビューをクエリー・リライトで使用できるようにするには、REFRESH FORCEを使用します。最適なリフレッシュ方法が選択されます。高速リフレッシュができない場合は、完全リフレッシュが行われます。

- 1回のプロシージャ・コールで、すべてのマテリアライズド・ビューをリフレッシュします。これにより、ネステッド・マテリアライズド・ビューで指定された依存性、および他のマテリアライズド・ビューに対するクエリー・リライトを使用した効率的なリフレッシュを考慮した正しい順序によるすべてのマテリアライズド・ビューのリフレッシュが、Oracleによってスケジューリングされます。

集計を含まないマテリアライズド・ビューのリフレッシュのヒント

結合を含み、集計を含まないマテリアライズド・ビューは、集計を含むマテリアライズド・ビューよりもかなりサイズが大きくなる傾向があるため、ディテール表の結合列ROWIDのそれぞれに索引を設定すると、リフレッシュ・パフォーマンスが大幅に向上します。たとえば、次のマテリアライズド・ビューを考えてみます。

CREATE MATERIALIZED VIEW detail_fact_mv BUILD IMMEDIATE AS
SELECT s.rowid "sales_rid", t.rowid "times_rid", c.rowid "cust_rid",
c.cust_state_province, t.week_ending_day, s.amount_sold
FROM sales s, times t, customers c
WHERE s.time_id = t.time_id AND s.cust_id = c.cust_id;

この場合は、sales_rid、times_ridおよびcust_rid列に索引を作成します。リフレッシュを起動する前に、セッションでのパラレルDMLを可能にするとともにパーティション化も行ってください。リフレッシュ・パフォーマンスが大幅に向上します。

このタイプのマテリアライズド・ビューは、DMLがディテール表に実行される場合でも高速リフレッシュも可能です。このタイプのマテリアライズド・ビューでも、単一表集計マテリアライズド・ビューと同じ手順に従ってください。つまり、1つのタイプの変更(ダイレクト・パス・インサートまたはDML)を行うたびにマテリアライズド・ビューをリフレッシュします。これは、Oracle Databaseが、1つのタイプの変更のみが行われたと検出した場合に、大幅な最適化を行うためです。

また、複数の表をすべてロードしてからリフレッシュを行うより、表を1つロードするたびにリフレッシュを起動することをお薦めします。

ON COMMITリフレッシュの場合、Oracleは、コミットされたトランザクションで実行されたDMLのタイプを記録します。そのため、同じトランザクションの他の表には、できるだけダイレクト・パス・ロードおよび従来型DMLを実行しないでください。Oracleがリフレッシュ・フェーズを最適化できない可能性があります。たとえば、次のような方法はお薦めできません。

1. ファクト表への新規データのダイレクト・ロード

2. 記憶域表へのDML

3. コミット

また、異なるタイプの従来型DML文は、できるだけ混在させないでください。これも、高速リフレッシュ時の様々な最適化を妨げる要因になります。たとえば、次のような文は使用しないでください。

1. ファクト表への挿入

2. ファクト表からの削除

3. コミット

多数の更新が必要な場合は、できるだけ1回のトランザクションにまとめてください。これによって、リフレッシュは、更新のたびにではなく、コミット時に1回のみで済みます。

データ・ウェアハウス環境では、マテリアライズド・ビューがパラレル句を含む場合、次の手順で行うことをお薦めします。

1. ファクト表にバルク・ロードします。

2. パラレルDMLを使用可能にします。

3. ALTER SESSION ENABLE PARALLEL DML文を発行します。

4. マテリアライズド・ビューをリフレッシュします。

ネステッド・マテリアライズド・ビューのリフレッシュのヒント

ネステッド・マテリアライズド・ビューのベースとなるオブジェクトは、マテリアライズド・ビューのリフレッシュ時にはすべて通常の表として扱われます。ON COMMITリフレッシュ・オプションが指定されている場合は、すべてのマテリアライズド・ビューがコミット時に適切な順番でリフレッシュされます。Oracleは、一部順序付けられたマテリアライズド・ビューの集合を作成し、リフレッシュ完了後にはすべてのマテリアライズド・ビューが最新になっているように、リフレッシュを行います。マテリアライズド・ビューの状態は、適切なビュー(USER、DBA_、ALL_MVIEWS)に問い合せることでチェックできます。

マテリアライズド・ビューのいずれかがON DEMANDリフレッシュとして定義されている場合(リフレッシュ方法がFAST、FORCE、COMPLETEのいずれであるかにかかわらず)、ネステッド・マテリアライズド・ビューは、(最新かどうかにかかわらず)他のマテリアライズド・ビューの現在の内容との比較によってリフレッシュされます。そのため、正しい順番で(マテリアライズド・ビュー間の依存性を考慮して)リフレッシュする必要があります。これには、ネストされた階層の一番上にあるマテリアライズド・ビューに対してリフレッシュ・プロシージャを呼び出し、nestedパラメータをTRUEに指定します。

コミット中にリフレッシュに失敗した場合は、リフレッシュされていないマテリアライズド・ビューのリストがアラート・ログに書き込まれます。それらをすべて依存マテリアライズド・ビューとともに手動でリフレッシュする必要があります。

ネステッド・マテリアライズド・ビューのリフレッシュには、通常のマテリアライズド・ビューの場合と同じDBMS_MVIEWプロシージャを使用します。

これらのプロシージャは、ネステッド・マテリアライズド・ビューに対して使用されると、次のように動作します。

- 他のマテリアライズド・ビュー上に作成されているマテリアライズド・ビューmy_mvにREFRESHが適用されると、my_mvは、nested => TRUEを指定しないかぎり、他のマテリアライズド・ビューの現在の内容を反映するようにリフレッシュされます(つまり、他のマテリアライズド・ビューが最初にリフレッシュされることはありません)。

- REFRESH_DEPENDENTをマテリアライズド・ビューmy_mvに適用すると、nested => TRUEを指定しないかぎり、my_mvに直接依存しているマテリアライズド・ビューのみがリフレッシュされます(my_mvに依存しているマテリアライズド・ビューにさらに依存しているマテリアライズド・ビューはリフレッシュされません)。

- REFRESH_ALL_MVIEWSを使用すると、マテリアライズド・ビューがリフレッシュされる順序は、ネステッド・マテリアライズド・ビュー間の依存性が考慮されます。

- GET_MV_DEPENDENCIESで、あるオブジェクトに直接依存するマテリアライズド・ビューのリストが作成されます。

UNION ALLでの高速リフレッシュのヒント

マテリアライズド・ビュー定義にメンテナンス列を指定することで、UNION ALLを使用するマテリアライズド・ビューに高速リフレッシュを使用できます。たとえば、UNION ALL演算子を持つマテリアライズド・ビューは、次のように高速リフレッシュできます。

CREATE MATERIALIZED VIEW fast_rf_union_all_mv AS
SELECT x.rowid AS r1, y.rowid AS r2, a, b, c, 1 AS marker
FROM x, y WHERE x.a = y.b
UNION ALL
SELECT p.rowid, r.rowid, a, c, d, 2 AS marker
FROM p, r WHERE p.a = r.y;

メンテナンス・マーカー列(例ではMARKER)の形式は、numeric_or_string_literal AS column_aliasである必要があります。ここで各UNION ALLメンバーは、numeric_or_string_literalに対して別個の値を持ちます。

コミットSCNベースのマテリアライズド・ビュー・ログを使用した高速リフレッシュのヒント

実表のマテリアライズド・ビュー・ログにWITH COMMIT SCN句を含めるようにすると、多くの場合、高速リフレッシュのパフォーマンスを大幅に改善できます。WITH COMMIT SCNを使用してマテリアライズド・ビュー・ログの処理を最適化することで、高速リフレッシュの処理時間を短縮できます。次の例は、この句の使用方法を示しています。

CREATE MATERIALIZED VIEW LOG ON sales WITH ROWID
(prod_id, cust_id, time_id, channel_id, promo_id, quantity_sold, amount_sold),
COMMIT SCN INCLUDING NEW VALUES;

マテリアライズド・ビューのリフレッシュではコミットSCNベースのマテリアライズド・ビュー・ログが自動的に使用されるので、リフレッシュ時間が短縮されます。

COMMIT SCNを利用できるのは、新しいマテリアライズド・ビュー・ログのみです。既存のマテリアライズド・ビュー・ログの場合は、削除して再作成しないかぎり、変更してCOMMIT SCNを追加できません。

タイムスタンプ・ベースのマテリアライズド・ビュー・ログを使用した実表とコミットSCNベースのマテリアライズド・ビュー・ログを使用した実表の両方でマテリアライズド・ビューが作成されると、エラー(ORA-32414)が発生し、これらのマテリアライズド・ビュー・ログが高速リフレッシュに関して相互に互換性がないことを示すメッセージが表示されます。

マテリアライズド・ビューのリフレッシュ後のヒント

ロードまたは増分ロードを行い、ディテール表索引を再作成した後は、整合性制約(ある場合)を使用可能に戻し、そのディテール表から導出されたマテリアライズド・ビューおよびマテリアライズド・ビュー索引をリフレッシュする必要があります。データ・ウェアハウス環境では、参照整合性制約は、通常、NOVALIDATEまたはRELYオプションで使用可能にできます。リフレッシュ操作を行う前に、そのリフレッシュ操作をリカバリ可能にする必要があるかどうかを決定する必要があります。マテリアライズド・ビューのデータは冗長で、いつでもディテール表から再作成できるため、マテリアライズド・ビューのロギングは使用禁止にすることをお薦めします。ロギングを無効にし、増分リフレッシュをリカバリの必要なしで実行するには、リフレッシュの前にALTER MATERIALIZED VIEW ... NOLOGGING文を使用します。

ON COMMIT方法でマテリアライズド・ビューをリフレッシュする場合は、リフレッシュ操作後、アラート・ログalert_SID.logおよびトレース・ファイルora_SID_number.trcで、エラーが発生していないかどうかをチェックします。

パーティション表付きマテリアライズド・ビューの使用

データ・ウェアハウスの主なメンテナンス・コンポーネントは、ディテール・データの変更時におけるマテリアライズド・ビューの同期化(リフレッシュ)です。このとき基礎となるディテール表をパーティション化していると、リフレッシュ・タスクの所要時間を短縮できます。これは、パーティション化によりパラレルDMLを使用してマテリアライズド・ビューを更新できるようになるためです。また、パーティション・チェンジ・トラッキングも使用可能になります。

パーティション・チェンジ・トラッキングの高速リフレッシュ

データ・ウェアハウスでは、通常、ディテール表の変更により、DROP、EXCHANGE、MERGEおよびADD PARTITIONなどのパーティション・メンテナンス操作が必要になります。このような操作の後でマテリアライズド・ビューをメンテナンスするには、手動メンテナンス(CONSIDER FRESHも参照)または完全リフレッシュを使用する必要がありました。現在では、パーティション・チェンジ・トラッキング(PCT)リフレッシュと呼ばれる高速リフレッシュ機能を使用できます。

PCTを使用可能にするには、ディテール表をパーティション化する必要があります。この機能では、マテリアライズド・ビュー自体をパーティション化する必要はありません。PCTリフレッシュが可能な場合は、ユーザーに対して透過的に処理が発生します。PCTの要件は、「パーティション・チェンジ・トラッキング」を参照してください。

この機能の使用例を次に示します。「PCT高速リフレッシュの使用例1」で、salesはtime_id列を使用してパーティション化された表で、productsはprod_category列でパーティション化されています。表timesはパーティション表ではありません。

PCT高速リフレッシュの使用例1

1. 次のマテリアライズド・ビューは、PCTの要件を満たしています。

CREATE MATERIALIZED VIEW cust_mth_sales_mv
BUILD IMMEDIATE
REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT s.time_id, s.prod_id, SUM(s.quantity_sold), SUM(s.amount_sold),
p.prod_name, t.calendar_month_name, COUNT(*),
COUNT(s.quantity_sold), COUNT(s.amount_sold)
FROM sales s, products p, times t
WHERE  s.time_id = t.time_id AND s.prod_id = p.prod_id
GROUP BY t.calendar_month_name, s.prod_id, p.prod_name, s.time_id;

2. DBMS_MVIEW.EXPLAIN_MVIEWプロシージャを使用すると、PCTリフレッシュが可能な表を判断できます。

MVNAME              CAPABILITY_NAME   POSSIBLE  RELATED_TEXT  MSGTXT
-----------------   ---------------   --------  ------------  ----------------
CUST_MTH_SALES_MV   PCT               Y         SALES
CUST_MTH_SALES_MV   PCT_TABLE         Y         SALES
CUST_MTH_SALES_MV   PCT_TABLE         N         PRODUCTS      no partition key
or PMARKER
in SELECT list
CUST_MTH_SALES_MV   PCT_TABLE         N         TIMES         relation is not
partitionedtable

EXPLAIN_MVIEWから抜粋したサンプル出力からもわかるように、sales表に対して実行されるパーティション・メンテナンス操作では、PCT高速リフレッシュが可能です。ただし、products表に対するパーティション・メンテナンス操作や更新の後は、cust_mth_sales_mvに十分な情報が含まれていないため、PCTリフレッシュは実行できません。times表はパーティション化されていないため、PCTリフレッシュできないことに注意してください。Oracle DatabaseでPCTリフレッシュが適用されるのは、更新されたすべての表に関してPCTをサポートするだけの十分な情報がマテリアライズド・ビューにあると判断できる場合です。DBA_MVIEWSやDBA_MVIEW_DETAIL_PARTITIONなどのビューを使用すると、最新および失効したパーティションを確認できます。

このプロシージャの使用方法およびPCT関連のビューの詳細は、「マテリアライズド・ビュー機能の分析」を参照してください。

3. 少し後の時点で、sales表の1つのパーティションのSPLIT操作が必要になったとします。

ALTER TABLE SALES
SPLIT PARTITION month3 AT (TO_DATE('05-02-1998', 'DD-MM-YYYY'))
INTO (PARTITION month3_1 TABLESPACE summ,
PARTITION month3 TABLESPACE summ);

4. sales表になんらかのデータを挿入します。

5. DBMS_MVIEW.REFRESHプロシージャを使用して、cust_mth_sales_mvを高速リフレッシュします。

EXECUTE DBMS_MVIEW.REFRESH('CUST_MTH_SALES_MV', 'F',
'',TRUE,FALSE,0,0,0,FALSE);

この使用例では、高速リフレッシュを行うとPCTリフレッシュが自動的に実行されます。ただし、パーティション・メンテナンス操作によって表が更新され、PCTが不可能な場合には、高速リフレッシュは行われません。これについては、「PCT高速リフレッシュの使用例2」を参照してください。

「PCT高速リフレッシュの使用例1」は、次のように、PMARKER句を使用してマテリアライズド・ビューを作成した場合にも該当します。

CREATE MATERIALIZED VIEW cust_sales_marker_mv
BUILD IMMEDIATE
REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT DBMS_MVIEW.PMARKER(s.rowid) s_marker, SUM(s.quantity_sold),
SUM(s.amount_sold), p.prod_name, t.calendar_month_name, COUNT(*),
COUNT(s.quantity_sold), COUNT(s.amount_sold)
FROM sales s, products p, times t
WHERE  s.time_id = t.time_id AND s.prod_id = p.prod_id
GROUP BY DBMS_MVIEW.PMARKER(s.rowid),
p.prod_name, t.calendar_month_name;

PCT高速リフレッシュの使用例2

「PCT高速リフレッシュの使用例2」の最初の3つの手順は、「PCT高速リフレッシュの使用例1」と同じです。その後、sales表に対するパーティションのSPLIT操作を実行します。ただし、マテリアライズド・ビューをリフレッシュする前に、times表にレコードを挿入します。

1. 「PCT高速リフレッシュの使用例1」の場合と同じです。

2. 「PCT高速リフレッシュの使用例1」の場合と同じです。

3. 「PCT高速リフレッシュの使用例1」の場合と同じです。

4. 「PCT高速リフレッシュの使用例1」と同じSPLIT操作の発行後に、times表になんらかのデータを挿入します。

ALTER TABLE SALES
SPLIT PARTITION month3 AT (TO_DATE('05-02-1998', 'DD-MM-YYYY'))
INTO (PARTIITION month3_1 TABLESPACE summ,
PARTITION month3 TABLESPACE summ);

5. cust_mth_sales_mvをリフレッシュします。

EXECUTE DBMS_MVIEW.REFRESH('CUST_MTH_SALES_MV', 'F',
'',TRUE,FALSE,0,0,0,FALSE);
ORA-12052: cannot fast refresh materialized view SH.CUST_MTH_SALES_MV

PCT高速リフレッシュを実行できない表に対してDMLが発生しているため、このマテリアライズド・ビューは高速リフレッシュできません。この状況を回避するには、PCT高速リフレッシュが使用可能になっているディテール表に対するパーティション・メンテナンス操作の直後に、高速リフレッシュを実行することをお薦めします。

「PCT高速リフレッシュの使用例2」の状況が発生した場合は、2つの方法が考えられます。つまり、完全リフレッシュを実行するか、後述のようにCONSIDER FRESHオプションに切り替えるかです。ただし、CONSIDER FRESHとパーティション・チェンジ・トラッキングの高速リフレッシュには、互換性がないため注意する必要があります。ALTER MATERIALIZED VIEW cust_mth_sales_mv CONSIDER FRESH文が発行されると、完全リフレッシュが完了するまで、このマテリアライズド・ビューにはPCTリフレッシュが適用されなくなります。さらに、手動でマテリアライズド・ビューをリフレッシュしないかぎり、CONSIDER FRESHを使用しないでください。

データ・ウェアハウスで一般的な状況は、データのローリング・ウィンドウを使用することです。この場合、たとえばディテール表とマテリアライズド・ビューに過去12か月分のデータが含まれる場合があります。毎月、1か月分の新規データが表に追加され、最も古い月が削除(またはアーカイブ)されます。PCTリフレッシュは、この場合にマテリアライズド・ビューをメンテナンスする非常に効率的なメカニズムです。

PCT高速リフレッシュの使用例3

1. 通常、新規データは、新規パーティションを追加し、それを新規データを含む表と交換することでディテール表に追加されます。

ALTER TABLE sales ADD PARTITION month_new ...
ALTER TABLE sales EXCHANGE PARTITION month_new month_new_table

2. 次に、最も古いパーティションが削除されるか切り捨てられます。

ALTER TABLE sales DROP PARTITION month_oldest;

3. ここで、マテリアライズド・ビューがPCTリフレッシュの要件をすべて満たしているとします。

EXECUTE DBMS_MVIEW.REFRESH('CUST_MTH_SALES_MV', 'F', '', TRUE, FALSE,0,0,0,FALSE);

高速リフレッシュでは、PCTが使用可能であることが自動的に検出され、PCTリフレッシュが実行されます。

CONSIDER FRESHの高速リフレッシュ

データ・ウェアハウスでは、通常、履歴情報がディテール表になくなっても、この情報をマテリアライズド・ビューに累積する必要があります。この場合は、ALTER MATERIALIZED VIEW materialized_view_name CONSIDER FRESH文を使用して、マテリアライズド・ビューをメンテナンスできます。

CONSIDER FRESHでは、マテリアライズド・ビューの内容がFRESHである(ディテール表と同期している)と宣言していることに注意してください。このオプションをこのような場合にクエリー・リライトと併用すると、予期しない結果になる可能性があるため注意が必要です。

履歴データがある場合にCONSIDER FRESHを使用した後、DMLやダイレクト・ロード後の従来の高速リフレッシュをマテリアライズド・ビューに適用することは可能ですが、PCT高速リフレッシュは実行できません。これは、ある時点にディテール表のパーティションに格納されていたデータが現在は集計形式でマテリアライズド・ビューに格納されていると、PCTリフレッシュではそのパーティションとマテリアライズド・ビューとの再同期化が試みられ、再計算できない履歴データが削除される可能性があるためです。

sales表に前年度のデータが格納されており、cust_mth_sales_mvには過去10年間のデータが集計形式で保持されているとします。

1. sales表のパーティションから古いデータを削除します。

ALTER TABLE sales TRUNCATE PARTITION month1;

マテリアライズド・ビューは、パーティション操作によって失効とみなされ、リフレッシュが必要です。ただし、ディテール表にはパーティションに関連したすべてのデータが含まれていないため、高速リフレッシュは試行できません。

2. そのため、マテリアライズド・ビューが最新のものであるとOracle Databaseが認識できるように、マテリアライズド・ビューを変更します。

ALTER MATERIALIZED VIEW cust_mth_sales_mv CONSIDER FRESH;

この文は、Oracle Databaseに対してcust_mth_sales_mvは意図した用途に使用できる最新のものであると通知します。ただし、マテリアライズド・ビューのステータスは、最新でも失効でもありません。かわりに、UNKNOWNになっています。マテリアライズド・ビューのクエリー・リライトがQUERY_REWRITE_INTEGRITY = stale_toleratedモードで使用可能になっている場合は、それがリライトに使用されます。

3. データをsalesに挿入します。

4. マテリアライズド・ビューをリフレッシュします。

EXECUTE DBMS_MVIEW.REFRESH('CUST_MTH_SALES_MV', 'F', '', TRUE, FALSE,0,0,0,FALSE);

高速リフレッシュでは、sales表に対してINSERT文のみが発生したことが検出されるため、このマテリアライズド・ビューは新規データで更新されます。ただし、マテリアライズド・ビューのステータスはUNKNOWNのままです。マテリアライズド・ビューをFRESHステータスに戻す唯一の方法は、完全リフレッシュを行うことです。これにより、マテリアライズド・ビューからも古いデータが削除されます。

17 チェンジ・データ・キャプチャ

チェンジ・データ・キャプチャでは、Oracleのリレーショナル表に対して追加、更新または削除されたデータを効率的に識別およびキャプチャし、アプリケーションやユーザーがこの変更データを使用できるようにします。

注意:
Oracle Change Data CaptureはOracle Databaseの今後のリリースでサポートされなくなり、Oracle GoldenGateに置き換わる予定です。そのため、新しいアプリケーションには、Oracle GoldenGateを使用することを強くお薦めします。
Oracle Database 11gリリース2(11.2)では、チェンジ・データ・キャプチャは引き続きこれまでのリリースのように機能します。現在チェンジ・データ・キャプチャを使用している場合、当面は使用し続けることができます。しかし、今後チェンジ・データ・キャプチャに機能強化は行われず、マニュアルに記載された、現在の機能に基づいてサポートされるだけになる予定です。

この章では、チェンジ・データ・キャプチャについて説明します。内容は次のとおりです。

- チェンジ・データ・キャプチャの概要

- チェンジ・ソースとチェンジ・データ・キャプチャのモード

- チェンジ・セット

- チェンジ・テーブル

- チェンジ・データ・キャプチャ環境に関する情報の取得

- 変更データの公開準備

- 変更データの公開

- 変更データのサブスクライブ

- 公開されたデータの管理

- 同期チェンジ・データ・キャプチャに関する考慮事項

- 非同期チェンジ・データ・キャプチャに関する考慮事項

- 実装とシステム構成

チェンジ・データ・キャプチャのパブリッシュおよびサブスクライブPL/SQLパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

チェンジ・データ・キャプチャの概要

データ・ウェアハウスでは、1つ以上の本番データベースからリレーショナル・データを抽出し、分析のためにデータ・ウェアハウスに転送することが必要になることがよくあります。チェンジ・データ・キャプチャは、変更があったデータのみを瞬時に識別して処理し、変更データをさらに利用できるようにします。

チェンジ・データ・キャプチャを使用しない変更データのキャプチャ

チェンジ・データ・キャプチャが導入される前は、変更データをキャプチャする方法として、表区別および変更値選択などいくつかの方法がありました。

表区別では、ソース(本番)データベースから、古いバージョンの表が存在している(変更データがキャプチャされる)ステージング・データベースへ表全体のコピーを転送します。SQLのMINUS演算子を次のような問合せで使用すると、挿入済の行および更新済の行の新しいバージョンを取得できます。

SELECT * FROM new_version
MINUS SELECT * FROM old_version;

次のような問合せでは、削除済の行および更新済の行の古いバージョンも取得できます。

SELECT * FROM old_version
MINUS SELECT * FROM new_version;

ただし、この方法には次のような問題点があります。

- 変更データのみでなく、表全体の新しいバージョンをステージング・データベースに転送する必要があるので、転送コストが大幅に増加します。

- ステージング・データベース上で2つのMINUS演算を実行する計算コストが非常に高くなる可能性があります。

- 表区別では、古い値に戻ったデータはキャプチャできません。たとえば、製品表の古いバージョンと新しいバージョンの間で製品の価格を何回か変更したとします。新しいバージョンにおける価格が古いバージョンにおける価格と最終的に同じになった場合、表区別では、その価格が変動したことを検出できません。製品表の古いバージョンから新しいバージョンにいたる中間の価格値も、表区別ではキャプチャできません。

- どの変更が同じトランザクションの一部として行われたかを判断する方法がありません。たとえば、営業部長が取引をまとめるために特別な値引きを作成するとします。値引きおよび売上の作成が同じトランザクションの一部として発生したファクトは、ソース・データベースをそうした目的で特別に設計していないかぎり、キャプチャできません。

変更値選択では、特定の列の値に基づいてソース・テーブルから新しいデータおよび変更済のデータを選択することによって、ソース・データベース上のデータをキャプチャします。たとえば、ソース・テーブルにLAST_UPDATE_DATEという列があるとします。変更をキャプチャするには、LAST_UPDATE_DATE列の値を基準にしてソース・テーブルからの選択を行います。

ただし、この方法にも次のような制限があります。

- 変更データをキャプチャするオーバーヘッドはソース・データベース上で負担する必要があり、コストが高くなる可能性のある問合せを、ソース・データベース上のソース・テーブルに対して実行する必要があります。これらの問合せが必要となることで、本来必要ではない索引を追加しなければならない場合があります。このオーバーヘッドをステージング・データベースにオフロードする方法はありません。

- この方法は、中間値のキャプチャにおいて表区別法と大差ありません。製品表の価格が変動する場合、中間の値をすべてキャプチャできないばかりか、価格が変更されたかどうかや、終了値が、前回変更データをキャプチャした際と同じかどうかを判別できません。

- この方法は、どのデータ変更が同じトランザクション内で一緒に行われたのかをキャプチャする点についても、表区別法と大差ありません。同じトランザクション内で一緒に発生した変更に関する情報をキャプチャする必要がある場合は、ソース・データベースをこの目的で特別に設計する必要があります。

- 変更値列の詳細度が、新しい行および変更済の行を一意に識別するのに十分な細かさでない場合があります。たとえば、次のように仮定します。

- LAST_UPDATE_DATEなどの日付列で変更値選択を使用してデータ変更をキャプチャする。

- キャプチャは、特定の瞬間(2003年2月14日17時10分00秒)に実行される。

- キャプチャを実行した同時刻に表に追加の更新が発生する。

次にデータ変更をキャプチャする際は、2003年2月14日17時10分00秒直後のLAST_UPDATE_DATEで行を選択するので、その秒の残りの間に発生した変更を見逃すことになります。

変更値選択を使用するには、この例外を受け入れるか、必要な詳細度の変更値列を人為的に追加するか、キャプチャ処理中のソース・テーブルへの変更をロックアウトする必要がありますが、これによって、ソース・データベースのパフォーマンスがさらに低下することになります。

- 変更データのキャプチャ元であるすべての表が変更値列を持つ必要があることを考慮して、あらかじめ、このキャプチャ・メカニズムでソース・データベースを設計する必要があります。レガシー・システムのデータソースでデータ・ウェアハウスを構築する場合、それらのレガシー・システムでは、必要とする変更値列が提供されない場合があります。

チェンジ・データ・キャプチャは、コストが高く扱いにくい表区別や変更値選択メカニズムに依存しません。そうしたメカニズムに依存することなく、ユーザー表に対して行ったINSERT、UPDATEおよびDELETE操作で生成された変更データをキャプチャします。この変更データはチェンジ・テーブルと呼ばれるリレーショナル表に格納され、制御された方法でアプリケーションやユーザーによって使用できるようになります。

チェンジ・データ・キャプチャを使用した変更データのキャプチャ

チェンジ・データ・キャプチャは、次のいずれかのモードでコミット済の変更データをキャプチャおよび公開できます。

- 同期
変更データは、データ操作言語(DML)操作(INSERT、UPDATEまたはDELETE)を行う各SQL文が実行されると、ソース・データベースのトリガーによってすぐにキャプチャされます。このモードでは、変更データは、ソース・テーブルを変更するトランザクションの一部としてキャプチャされます。同期チェンジ・データ・キャプチャは、OracleのStandard EditionおよびEnterprise Editionで使用できます。このモードの詳細は、「同期チェンジ・データ・キャプチャ」を参照してください。

- 非同期
変更データは、REDOログ・ファイルに送られたデータを利用することによって、DML操作を実行するSQL文がコミットされた後にキャプチャされます。このモードでは、変更データは、ソース・テーブルを変更するトランザクションの一部としてキャプチャされないので、トランザクションに影響を与えません。

非同期チェンジ・データ・キャプチャには、HotLog、分散HotLogおよびAutoLogの3つのモードがあります。これらのモードの詳細は、「非同期チェンジ・データ・キャプチャ」を参照してください。

非同期チェンジ・データ・キャプチャは、Oracle Streamsに対して構築され、リレーショナル・インタフェースを提供します。Oracle Streamsの詳細は、『Oracle Streams概要および管理』を参照してください。

チェンジ・データ・キャプチャを使用して変更データをキャプチャするメリットとして、次のようなことがあげられます。

- 完全性
チェンジ・データ・キャプチャは、UPDATE操作前後のデータ値を含む、INSERT、UPDATEおよびDELETE操作のすべての結果をキャプチャできます。

- パフォーマンス
非同期チェンジ・データ・キャプチャは、ソース・データベースに対するパフォーマンスの影響が最小となるように構成できます。

- インタフェース
チェンジ・データ・キャプチャには、パブリッシュやサブスクライブを簡単に実行できるインタフェースとなるDBMS_CDC_PUBLISHパッケージとDBMS_CDC_SUBSCRIBEパッケージがあります。

- コスト
チェンジ・データ・キャプチャは、データベースからの変更データの抽出を簡素化し、また、Oracle Databaseの一部でもあるため、オーバーヘッド・コストを軽減します。

同期チェンジ・データ・キャプチャのソース・テーブルとして、透過的なデータ暗号化を使用する表は使用できません。非同期チェンジ・データ・キャプチャについては、ソース・データベースとステージング・データベースの両方でCOMPATIBLEが11以上に設定されている場合は透過的なデータ暗号化をサポートします。チェンジ・データ・キャプチャでは、チェンジ・テーブル内のデータの暗号化は行われません。チェンジ・テーブルの列を暗号化するには、ALTER TABLE文を使用してユーザーが手動で暗号化します。Oracle Streamsの詳細は、『Oracle Streams概要および管理』を参照してください。

チェンジ・データ・キャプチャ・システムでは、パブリッシャとサブスクライバの相互作用に基づいて変更データがキャプチャされ、配布されます。次の項を参照してください。

パブリッシュおよびサブスクライブのモデル

ほとんどのチェンジ・データ・キャプチャ・システムでは、1人のパブリッシャが変更データをキャプチャして公開します。変更データには、複数のサブスクライバ(アプリケーションやユーザー)がアクセスする場合があります。チェンジ・データ・キャプチャには、パブリッシュおよびサブスクライブ・タスクのためにPL/SQLパッケージが用意されています。

次の項では、パブリッシャおよびサブスクライバの役割を詳細に説明します。後続の項では、チェンジ・ソース、チェンジ・データ・キャプチャのモードの詳細、およびチェンジ・テーブルについて説明します。

パブリッシャ

通常、パブリッシャは、チェンジ・データ・キャプチャ・システムを構成するスキーマ・オブジェクトの作成とメンテナンスを行うデータベース管理者(DBA)です。通常、分散HotLogを除くチェンジ・データ・キャプチャのすべてのモードでは、ステージング・データベースに1人のパブリッシャが存在します。チェンジ・データ・キャプチャの分散HotLogモードでは、ソース・データベースおよびステージング・データベースに1人のパブリッシャが定義されている必要があります。次のリストでは、ソース・データベース、ステージング・データベース、およびチェンジ・データ・キャプチャに必要なそれぞれのデータベースのオブジェクトについて説明します。

- ソース・データベース
これは、対象データが格納されている本番データベースです。チェンジ・データ・キャプチャに必要な次のオブジェクトは、ソース・データベースにあります。

- ソース・テーブル
ソース・テーブルは対象データが格納されている本番データベースの表です。ソース・データベースのすべての表、または表のサブセットです。

- REDOログ・ファイル
チェンジ・データ・キャプチャの非同期モードでは、変更データは、オンラインREDOログ・ファイルまたはアーカイブREDOログ・ファイル(またはその両方)から収集されます。非同期AutoLogモードでは、アーカイブREDOログ・ファイルは、ソース・データベースからステージング・データベースにコピーされます。

- チェンジ・ソース
チェンジ・ソースは、ソース・データベースの論理表現です。ソースを表す方法は、チェンジ・データ・キャプチャのモードによって異なります。

チェンジ・ソースは、チェンジ・データ・キャプチャの非同期分散HotLogモードにおいてのみ、ソース・データベースにあります。チェンジ・データ・キャプチャのその他のモードでは、チェンジ・ソースはステージング・データベースにあります。

分散HotLogチェンジ・ソースの詳細は、「非同期分散HotLogモード」を参照してください。

- ステージング・データベース
これは、キャプチャした変更データが適用されるデータベースです。パブリッシャが使用するキャプチャ・モードに応じて、ステージング・データベースは、ソース・データベースと同じ場合もあれば、異なる場合もあります。次のチェンジ・データ・キャプチャ・オブジェクトは、ステージング・データベースにあります。

- チェンジ・テーブル
チェンジ・テーブルは、単一のソース・テーブルの変更データがロードされるリレーショナル表です。サブスクライバに対して、チェンジ・テーブルはパブリケーションとして知られています。

- チェンジ・セット
チェンジ・セットは、トランザクション一貫性が保証される変更データのセットです。これには、1つ以上のチェンジ・テーブルが含まれます。

- チェンジ・ソース
チェンジ・ソースは、チェンジ・データ・キャプチャの次のモードでは、ステージング・データベースにあります。

- 同期 - 詳細は、「同期チェンジ・データ・キャプチャ」を参照してください。

- 非同期HotLog - 詳細は、「非同期HotLogモード」を参照してください。

- 非同期AutoLog - 詳細は、「非同期AutoLogモード」を参照してください。

次に、パブリッシャが実行する主なタスクを示します。

- サブスクライバが変更データを参照するために必要なソース・データベースとソース・テーブル、および変更データをキャプチャするモード(同期またはいずれかの非同期モード)を判断します。

- オラクル社が提供するパッケージDBMS_CDC_PUBLISHを使用して、対象のソース・テーブルから変更データをキャプチャするようにシステムをセットアップします。

- SQL文のGRANTおよびREVOKEを使用して、ユーザーやロールに対してチェンジ・テーブルへのSELECT権限の付与および取消しを行うことで、サブスクライバにチェンジ・テーブル内の変更データへの制御付きのアクセスを許可します。ただし、サブスクライバが変更データにアクセスするには、チェンジ・テーブルを直接使用するのではなく、ビューを使用することに注意してください。

図17-1では、パブリッシャは、サブスクライバがHQソース・データベースの変更データを参照する必要があることを判断します。特に、サブスクライバが必要なのは、sh.salesおよびsh.promotionsソース・テーブルの変更データです。

パブリッシャは、変更データのキャプチャに非同期AutoLogモードを使用することを決定します。パブリッシャは、DWステージング・データベース上に、1つのチェンジ・ソースHQ_SRC、1つのチェンジ・セットSH_SET、およびsales_ctとpromo_ctの2つのチェンジ・テーブルを作成します。sales_ctチェンジ・テーブルには、ソース・テーブルsh.salesの列がすべて含まれます。一方、promo_ctチェンジ・テーブルについては、パブリッシャはPROMO_COST列を除外することにしました。

図17-1 チェンジ・データ・キャプチャ・システムのパブリッシャ・コンポーネント

サブスクライバ

サブスクライバは、パブリッシュされた変更データのコンシューマです。サブスクライバはまた、次のタスクを実行します。

- オラクル社が提供するパッケージDBMS_CDC_SUBSCRIBEを次の目的に使用します。

- サブスクリプションの作成
サブスクリプションは、単一チェンジ・セット内の1つ以上の対象ソース・テーブルの変更データに対するアクセスを制御します。サブスクリプションには1つ以上のサブスクライバ・ビューが含まれます。

サブスクライバ・ビューは、サブスクリプションにおける特定のパブリケーションからの変更データを指定するビューです。サブスクライバは、パブリッシャが公開し、使用するアクセス権がサブスクライバに付与された変更データの参照において制限を受けます。サブスクライバ・ビューを指定する方法の選択に関する詳細は、「変更データのサブスクライブ」を参照してください。

- 変更データのセットを受け取る準備ができた時点でのチェンジ・データ・キャプチャへの通知
サブスクリプション・ウィンドウでは、サブスクライバ・ビューでサブスクライバが現在参照できるパブリケーションの行のタイム・レンジが定義されます。サブスプリクション・ウィンドウ内の最も古い行が低位限界と呼ばれ、最も新しい行が高位限界と呼ばれます。各サブスクリプションは、そのサブスクライバ・ビューすべてに適用される独自のサブスクリプション・ウィンドウを持ちます。

- 変更データのセットに関する作業が完了した時点でのチェンジ・データ・キャプチャへの通知

- SELECT文を使用して、サブスクライバ・ビューから変更データを取り出します。

サブスクライバは、サブスクライバが実行されているユーザー・アカウントの権限、およびサブスクライバに付与されているすべての追加の権限を所有しています。

図17-2において、サブスクライバが必要なのは、図17-1のパブリッシャが公開している列のサブセットです。図17-2に示されているパブリケーションは、図17-1ではチェンジ・テーブルとして表されていることに注意してください。これは、サブスクライバとパブリッシャそれぞれで使用される用語が異なることによるものです。

サブスクライバは、サブスクリプションsales_promos_listおよび2つのサブスクライバ・ビュー(spl_salesとspl_promos)を、DWステージング・データベース上のSH_SETチェンジ・セットに作成します。各サブスクライバ・ビュー内に、サブスクライバは、パブリッシャによって使用可能とされた列のサブセットを含めます。パブリッシャがPROMO_COST列を含むチェンジ・テーブルを作成しなかったため、サブスクライバはその列の変更データを参照できません。サブスクライバは、パブリッシャが設定したチェンジ・データ・キャプチャのモードを認識する必要はありません。

図17-2 チェンジ・データ・キャプチャ・システムのサブスクライバ・コンポーネント

サブスクライバにとって、チェンジ・データ・キャプチャは次のような利点があります。

- 各サブスクライバは、すべての変更の参照が保証されます。

- 複数のサブスクライバが追跡され、各サブスクライバには変更データへの共有アクセス権が付与されます。

- すべてのサブスクライバに不要になったデータがチェンジ・テーブルから自動的に削除されることによって、すべての記憶域管理が処理されます。チェンジ・データ・キャプチャは、パージ処理のために、24時間に1回実行されるジョブ・キューのジョブを開始します。このジョブでは多くの不具合が発生する可能性があるため(ジョブが削除されたり、スケジュールが変更される場合など)、この自動処理は、開始および実行されているジョブ・キュー・プロセス、および、存在しているチェンジ・データ・キャプチャのジョブに依存します。また、ロジカル・スタンバイ環境では、パージ・ジョブは実行されません。

注意:
Oracleでは、サブスクライバがサブスクライバ・ビューを介して変更データにアクセスする際にのみ、これらの利点が提供されます。

チェンジ・ソースとチェンジ・データ・キャプチャのモード

チェンジ・データ・キャプチャには、変更データのキャプチャ用に同期モードと非同期モードがあります。次の項では、チェンジ・データ・キャプチャの各モードがどのように実行されるか、およびチェンジ・データ・キャプチャの各モードに関連付けられたチェンジ・ソースの概要について説明します。

同期チェンジ・データ・キャプチャ

同期モードでは、変更データのキャプチャにソース・データベースのトリガーが使用されます。このモードでは、変更データがソース・データベース上で継続的にリアルタイムでキャプチャされるため、待機時間がありません。チェンジ・テーブルは、ソース・テーブルでDML操作がコミットされた際に移入されます。

ソース・データベースを表す、事前定義の同期チェンジ・ソースSYNC_SOURCEが1つあります。これは唯一の同期チェンジ・ソースです。変更や削除はできません。

チェンジ・データ・キャプチャの同期モードでは、キャプチャ時にソース・データベースのオーバーヘッドが増加しますが、変更データの抽出の簡素化により(表区別や変更値選択を使用して変更データを抽出するのと比較して)コストを削減できます。

チェンジ・データ・キャプチャのこのモードのチェンジ・テーブルは、ソース・データベースにローカルに存在する必要があります。

図17-3に、同期モードの構成を示します。ソース・テーブルで発生したDML操作の後に実行されたトリガーによって、SYNC_SOURCEチェンジ・ソース内のチェンジ・セットにチェンジ・テーブルが移入されます。

図17-3 同期チェンジ・データ・キャプチャ構成

非同期チェンジ・データ・キャプチャ

非同期モードでは、変更がソース・データベースに対してコミットされた後にデータベースのREDOログ・ファイルから変更データがキャプチャされます。

チェンジ・データ・キャプチャの非同期モードは、ソース・データベースで有効化されたサプリメンタル・ロギングのレベルに依存します。サプリメンタル・ロギングによって、ソース・データベースでREDOロギングのオーバーヘッドが増加するので、チェンジ・データ・キャプチャを使用するアプリケーションやユーザーのニーズと注意深くバランスを取る必要があります。サプリメンタル・ロギングの詳細は、「非同期チェンジ・データ・キャプチャとサプリメンタル・ロギング」を参照してください。

以降の項では、変更データのキャプチャにおける次の3つのモードについて説明します。

- 非同期HotLogモード

- 非同期分散HotLogモード

- 非同期AutoLogモード

非同期HotLogモード

非同期HotLogモードでは、変更データは、ソース・データベース上のオンラインREDOログ・ファイルからキャプチャされます。ソース・テーブル・トランザクションのコミット操作から変更データの取得までの間にはわずかな待機時間があります。

ソース・データベースの現在のオンラインREDOログ・ファイルを表す、事前定義のHotLogチェンジ・ソースHOTLOG_SOURCEが1つあります。これは唯一のHotLogチェンジ・ソースです。変更や削除はできません。

チェンジ・データ・キャプチャのこのモードのチェンジ・テーブルは、ソース・データベースにローカルに存在する必要があります。

図17-4に、非同期HotLogモードの構成を示します。コミット済トランザクションは、ログ・ライター・プロセス(LGWR)によって、ソース・データベース上のオンラインREDOログ・ファイルに記録されます。チェンジ・データ・キャプチャでは、トランザクションが新たにコミットされると、Oracle Streamsプロセスが使用され、HOTLOG_SOURCEチェンジ・ソース内のチェンジ・セットにチェンジ・テーブルが自動的に移入されます。

図17-4 非同期HotLog構成

非同期分散HotLogモード

非同期分散HotLogモードでは、変更データは、ソース・データベース上のオンラインREDOログ・ファイルからキャプチャされます。

事前定義の分散HotLogチェンジ・ソースはありません。分散HotLogモードでは、チェンジ・データ・キャプチャの他のモードとは異なり、ソース・データベースとステージング・データベースにまたがって、チェンジ・データ・キャプチャのアクティビティおよびオブジェクトが分散されます。チェンジ・ソースは、ステージング・データベースのパブリッシャによってソース・データベース上に定義されます。

分散HotLogチェンジ・ソースは、ソース・データベースの現在のオンラインREDOログ・ファイルを表します。ただし、ステージング・データベースのパブリッシャは、それぞれ異なるステージング・データベースのチェンジ・セットを含む分散HotLogチェンジ・ソースを複数定義できます。ソース・データベースおよびステージング・データベースは、異なるハードウェアのプラットフォーム上に配置でき、異なるオペレーティング・システム上で動作できますが、これにはいくつかの制限が適用されます。これらの制限については、「サポートされる分散HotLog構成および制限の概要」を参照してください。

図17-5に、非同期分散HotLogモードの構成を示します。ソース・データベース上のチェンジ・ソースは、オンラインREDOログ・ファイルから変更データをキャプチャし、Oracle Streamsを使用して、その変更データをステージング・データベース上のチェンジ・セットに伝播します。ステージング・データベースのチェンジ・セットは、チェンジ・セット内にチェンジ・テーブルを移入します。

チェンジ・データ・キャプチャのこのモードでは、2人(ソース・データベースとステージング・データベースに各1人ずつ)のパブリッシャが必要となります。ソース・データベースのパブリッシャは、ステージング・データベースのパブリッシャとしてステージング・データベースに接続するソース・データベースのデータベース・リンクを定義します。ステージング・データベースのパブリッシャは、ソース・データベースのパブリッシャとしてソース・データベースに接続するステージング・データベースのデータベース・リンクを定義します。公開操作はすべて、ステージング・データベースのパブリッシャが実行します。詳細は、「非同期分散HotLogの公開の実行」を参照してください。

図17-5 非同期分散HotLog構成

非同期AutoLogモード

非同期AutoLogモードでは、変更データは、REDO転送サービスによって管理されるREDOログ・ファイルのセットからキャプチャされます。REDO転送サービスは、ソース・データベースからステージング・データベースへのREDOログ・ファイルの自動転送を制御します。パブリッシャは、データベース初期化パラメータ(「非同期AutoLogの公開用初期化パラメータ」を参照)を使用して、REDOログ・ファイルがソース・データベース・システムからステージング・データベース・システムにコピーされ、自動的に登録されるようにREDO転送サービスを構成します。非同期AutoLogモードでは、ソース・データベースのオンラインREDOログかアーカイブREDOログのいずれかから変更データを取得できます。これらのオプションは、非同期AutoLogオンラインおよび非同期AutoLogアーカイブと呼ばれます。AutoLogオンライン・オプションを指定すると、ソース・データベースのオンラインREDOログから、ステージング・データベースのスタンバイREDOログにREDOデータをコピーするようにREDO転送サービスが設定されます。この場合、チェンジ・セットは、個々のソース・データベースのトランザクションのコミット後に移入されます。任意のステージング・データベースにはAutoLogオンライン・チェンジ・ソースを1つのみ含めることができ、チェンジ・セットを1つのみ含めることができます。AutoLogアーカイブ・オプションを指定すると、ソース・データベースからステージング・データベースにアーカイブREDOログがコピーされるようにREDO転送サービスが設定されます。この場合、チェンジ・セットは、新しいアーカイブREDOログ・ファイルがステージング・データベースに届くと移入されます。待機時間の程度は、ソース・データベース上のREDOログ・ファイルのスイッチの頻度に依存します。AutoLogアーカイブ・オプションは、AutoLogオンライン・オプションよりも待機時間の程度が大きくなりますが、任意のステージング・データベースには必要な数のAutoLogアーカイブ・チェンジ・ソースを含めることができます。

事前定義のAutoLogチェンジ・ソースはありません。AutoLogチェンジ・ソースを作成するソース・データベースに関する情報は、パブリッシャによって提供されます。詳細は、「非同期AutoLogの公開の実行」を参照してください。

図17-6は、チェンジ・データ・キャプチャの非同期AutoLogオンライン構成を示しています。この構成では、ソース・データベース上のLGWRプロセスによって、LOG_ARCHIVE_DEST_2パラメータで指定されているように、ソース・データベースのオンラインREDOログ・ファイルおよびステージング・データベースのスタンバイREDOログ・ファイルの両方にREDOデータがコピーされます。図では、このパラメータをLOG_ARCHIVE_DEST_2として示していますが、この整数値には、1から10の間の任意の値を指定できます。

LGWRプロセスはOracle Netを使用して、REDOデータをネットワークからリモート・ファイル・サーバー(RFS)プロセスに送信することに注意してください。REDOデータをリモートの宛先に送信するには、Oracle Netを介した途切れることのない接続性が必要となります。

ステージング・データベースでは、RFSプロセスによって、REDOデータがスタンバイREDOログ・ファイルに書き込まれます。次に、チェンジ・データ・キャプチャは、Oracle Streamsのダウンストリーム・キャプチャを使用して、AutoLogチェンジ・ソース内のチェンジ・セットにチェンジ・テーブルを移入します。

ソース・データベースとステージング・データベースが動作するハードウェア、オペレーティング・システム、およびOracleバージョンは同じである必要があります。

図17-6 非同期Autologオンライン・チェンジ・データ・キャプチャ構成

図17-7は、典型的なチェンジ・データ・キャプチャの非同期AutoLogアーカイブ構成です。この構成では、ソース・データベース上でのREDOログ・ファイル・スイッチ時に、アーカイバ・プロセスによって、ソース・データベース上のREDOログ・ファイルがLOG_ARCHIVE_DEST_1パラメータの指定先にアーカイブされ、LOG_ARCHIVE_DEST_2パラメータで指定したステージング・データベースにコピーされます。図では、これらのパラメータをLOG_ARCHIVE_DEST_1およびLOG_ARCHIVE_DEST_2として示していますが、これらのパラメータ文字列の整数値には、1から10の間の任意の値を指定できます。

アーカイバ・プロセスはOracle Netを使用して、REDOデータをネットワークからリモート・ファイル・サーバー(RFS)プロセスに送信することに注意してください。REDOログ・ファイルをリモートの宛先に送信するには、Oracle Netを介した途切れることのない接続性が必要となります。

ステージング・データベースでは、RFSプロセスによって、REDOデータがコピー済ログ・ファイルに書き込まれます。次に、チェンジ・データ・キャプチャは、Oracle Streamsのダウンストリーム・キャプチャを使用して、AutoLogチェンジ・ソース内のチェンジ・セットにチェンジ・テーブルを移入します。

REDO転送サービスの詳細は、『Oracle Data Guard概要および管理』を参照してください。

図17-7 非同期Autologアーカイブ・チェンジ・データ・キャプチャ構成

チェンジ・セット

チェンジ・セットは、トランザクション一貫性が保証され、1単位として管理可能な変更データの論理グループです。チェンジ・セットは、1つの(唯一の)チェンジ・ソースのメンバーです。

注意:
次の条件を満たす場合、チェンジ・データ・キャプチャのチェンジ・ソースに1つ以上のチェンジ・セットを含めることができます。

1. 分散HotLogチェンジ・ソースのすべてのチェンジ・セットは同じステージング・データベース上に存在する必要がある。

2. AutoLogオンライン・チェンジ・ソースに含めることができるチェンジ・セットは1つのみである。

パブリッシャが、同じチェンジ・セットに2つ以上のチェンジ・テーブルを含める場合、サブスクライバはチェンジ・セット内で表される表間で結合操作を実行でき、トランザクション一貫性を保証されます。

理論上は、チェンジ・セットは、そのチェンジ・ソースと同じモードを共有します。たとえば、AutoLogチェンジ・セットは、AutoLogチェンジ・ソースに格納されているチェンジ・セットです。パブリッシャは、DBMS_CDC_PUBLISH.CREATE_CHANGE_SETパッケージを使用してチェンジ・セットを定義できます。同期チェンジ・データ・キャプチャの場合、パブリッシャは事前定義のチェンジ・セットSYNC_SETも使用できます。ただし、SYNC_SETチェンジ・セットの変更または削除はできません。

パブリッシャは、チェンジ・セットのチェンジ・テーブルが無制限に大きくならないように、チェンジ・セットのレベルで、チェンジ・テーブルから不要な変更データをパージできます。変更データのパージに関する詳細は、「チェンジ・テーブルの不要なデータのパージ」を参照してください。

チェンジ・ソースとチェンジ・セットの有効な組合せ

表17-1に、チェンジ・ソースとチェンジ・セットの有効な組合せの概要と、それぞれが事前定義かパブリッシャ定義かを示します。また、チェンジ・ソースによって表されるソース・データベースが、ステージング・データベースからリモートかローカルかを示し、チェンジ・ソースが、チェンジ・データ・キャプチャの同期または非同期のどちらのモードで使用されるかを示します。

表17-1 チェンジ・ソースとチェンジ・セットの概要

モード

チェンジ・ソース

表されるソース・データベース

関連チェンジ・セット

同期

事前定義のSYNC_SOURCE

ローカル

事前定義のSYNC_SETおよびパブリッシャ定義。

非同期HotLog

事前定義のHOTLOG_SOURCE

ローカル

パブリッシャ定義。

非同期分散HotLog

パブリッシャ定義。

リモート

パブリッシャ定義。チェンジ・セットはすべて、同じステージング・データベース上に存在する必要がある。

非同期AutoLogオンライン

パブリッシャ定義。

リモート

パブリッシャ定義。AutoLogオンライン・チェンジ・ソースに含めることができるチェンジ・セットは1つのみ。

非同期AutoLogアーカイブ

パブリッシャ定義。

リモート

パブリッシャ定義。

チェンジ・テーブル

特定のチェンジ・テーブルには、特定のソース・テーブルに対して実行されたDML操作の結果として生成された変更データが含まれています。チェンジ・テーブルは、データベース表に格納されている変更データ自体と、制御列が含まれるチェンジ・テーブルのメンテナンスに必要なシステム・メタデータで構成されています。

パブリッシャは、チェンジ・テーブルに含めるソース列を指定します。通常、チェンジ・テーブルに有益なデータを含めるには、パブリッシャは、主キー列とサブスクライバが必要とするその他の列をチェンジ・テーブルに含める必要があります。たとえば、サブスクライバがsh.costs表のUNIT_COST列とUNIT_PRICE列に発生する変更を必要とすると仮定します。パブリッシャがチェンジ・テーブルにPROD_ID列を含めないと、サブスクライバは、ある製品の単位原価および単位価格が変更になったということしかわからず、これらの変更がどの製品に対して発生したかは判別できません。

制御列にはオプションと必須のものがあります。必須の制御列は、常にチェンジ・テーブルに含まれます。オプションの制御列は、チェンジ・テーブルを作成する際にパブリッシャが指定した場合に含まれます。制御列はチェンジ・データ・キャプチャによって管理されます。制御列の詳細は、「チェンジ・テーブルの制御列の理解」および「TARGET_COLMAP$およびSOURCE_COLMAP$の値の理解」を参照してください。

チェンジ・データ・キャプチャ環境に関する情報の取得

チェンジ・データ・キャプチャ環境に関する情報は、表17-2および表17-3に示す静的データ・ディクショナリ・ビューに用意されています。表17-2は、パブリッシャによる使用を対象としたビューをまとめたものです。この表のビューにアクセスするには、SELECT_CATALOG_ROLE権限が必要となります。表17-3は、サブスクライバによる使用を対象としたビューの一覧です。表17-3のビューは、接頭辞ALLまたはUSERが付いています。これらの接頭辞は一般的に次の意味を持ちます。

- ALL接頭辞の付いたビューでは、ユーザーがアクセスできるすべての情報を表示できます。この情報には、現行ユーザー・スキーマの情報および他のスキーマのオブジェクトの情報が含まれます。表示できるのは、現行ユーザーが権限やロールの付与によってこれらのオブジェクトに対するアクセス権を所有している場合です。

- USER接頭辞の付いたビューでは、追加の特別な権限やロールを使用せずに問合せを発行しているユーザーのスキーマの情報をすべて表示できます。

注意:
(パブリッシャとサブスクライバの両方を対象にしている)すべてのビューを表示するには、SELECT_CATALOG_ROLE権限が必要です。

表17-2 チェンジ・データ・キャプチャのパブリッシャによる使用を対象としたビュー

ビュー名

説明

ALL_CHANGE_SOURCES

既存のチェンジ・ソースを表示します。

ALL_CHANGE_PROPAGATIONS

ソース・データベース上の特定の分散HotLogチェンジ・ソースに関連するOracle Streamsの伝播を表示します。このビューは、リリース11.1または11.2のチェンジ・ソースではソース・データベース上に移入され、9.2、10.1または10.2のチェンジ・ソースではステージング・データベース上に移入されます。

ALL_CHANGE_PROPAGATION_SETS

ステージング・データベース上の特定の分散HotLogチェンジ・セットに関連するOracle Streamsの伝播を表示します。このビューは、リリース11.1または11.2のチェンジ・ソースではソース・データベース上に移入され、9.2、10.1または10.2のチェンジ・ソースではステージング・データベース上に移入されます。

ALL_CHANGE_SETS

既存のチェンジ・セットを表示します。

ALL_CHANGE_TABLES

既存のチェンジ・テーブルを表示します。

DBA_SOURCE_TABLES

データベース内の公開済のソース・テーブルをすべて表示します。

DBA_PUBLISHED_COLUMNS

データベース内の公開済のソース・テーブルの列をすべて表示します。

DBA_SUBSCRIPTIONS

サブスクリプションをすべて表示します。

DBA_SUBSCRIBED_TABLES

任意のサブスクライバがサブスクライブしたソース・テーブルをすべて表示します。

DBA_SUBSCRIBED_COLUMNS

任意のサブスクライバがサブスクライブしたソース・テーブルの列を表示します。

表17-3 チェンジ・データ・キャプチャのサブスクライバによる使用を対象としたビュー

ビュー名

説明

ALL_SOURCE_TABLES

現行ユーザーが所有するチェンジ・テーブルの公開ソース・テーブルをすべて表示します。

USER_SOURCE_TABLES

現行ユーザーが所有するチェンジ・テーブルの公開ソース・テーブルをすべて表示します。

ALL_PUBLISHED_COLUMNS

現行ユーザーが所有するチェンジ・テーブルのソース・テーブルの公開済の列をすべて表示します。脚注1

USER_PUBLISHED_COLUMNS

現行ユーザーが所有するチェンジ・テーブルのソース・テーブルの公開済の列をすべて表示します。

ALL_SUBSCRIPTIONS

現行ユーザーが作成したサブスクリプションをすべて表示します。

USER_SUBSCRIPTIONS

現行ユーザーが作成したサブスクリプションをすべて表示します。

ALL_SUBSCRIBED_TABLES

現行ユーザーがサブスクライブしたソース・テーブルを表示します。

USER_SUBSCRIBED_TABLES

現行ユーザーがサブスクライブしたソース・テーブルを表示します。

ALL_SUBSCRIBED_COLUMNS

現行ユーザーがサブスクライブしたソース・テーブルの列を表示します。

USER_SUBSCRIBED_COLUMNS

現行ユーザーがサブスクライブしたソース・テーブルの列を表示します。

脚注1ALL_PUBLISHED_COLUMNS、 USER_PUBLISHED_COLUMNSおよび*_SOURCE_TABLESのビューはシノニムなので、ユーザーには表または列の情報を表示する所有権が必要です。

これらのビューの詳細は、『Oracle Databaseリファレンス』を参照してください。

変更データの公開準備

この項では、パブリッシャの作成に関する情報、変更データをキャプチャするモードの選択に関する情報、チェンジ・データ・キャプチャの非同期分散HotLogモードで必要なデータベース・リンクの設定手順、およびチェンジ・データ・キャプチャで必要なデータベース初期化パラメータの設定手順など、変更データの公開を開始する前に、パブリッシャが実行する必要のあるタスクについて説明します。

パブリッシャは公開の実際の手順を実行する前に、次のタスクを行う必要があります。

- サブスクライバからの要件の収集

- 関連ソース・テーブルを含むソース・データベースの判別

- キャプチャ・モード(「データをキャプチャするモードの決定」で説明している同期、非同期HotLog、非同期分散HotLog、または非同期AutoLog)の選択

- ソース・データベースおよびステージング・データベースのDBAがデータベース初期化パラメータを設定していることの確認(「チェンジ・データ・キャプチャの公開用初期化パラメータの設定」および「変更データの公開」を参照)

- ソース・データベースからステージング・データベースへのデータベース・リンクおよびステージング・データベースからソース・データベースへのデータベース・リンクの設定(「非同期分散HotLogの公開の実行」を参照)。これには、ソース・データベースのパブリッシャがステージング・データベースのパブリッシャのユーザー名およびパスワードを知っている必要があり、ステージング・データベースのパブリッシャがソース・データベースのパブリッシャのユーザー名およびパスワードを知っている必要があります。

パブリッシャとなるユーザーの作成

チェンジ・データ・キャプチャのすべてのモードにおいて、ステージング・データベースDBAは、チェンジ・データ・キャプチャのパブリッシャとなるユーザーを作成します。また、チェンジ・データ・キャプチャの非同期分散HotLogモードでは、ソース・データベースDBAも、パブリッシャとなるユーザーを作成します。このパブリッシャがソース・データベースで実行するタスクは、ソース・データベースからステージング・データベースへのデータベース・リンクの作成のみです。

SYSおよびSYSTEMユーザーは、チェンジ・データ・キャプチャのパブリッシャとして使用できません。また、チェンジ・データ・キャプチャのパブリッシャは、デフォルト表領域としてSYSTEM表領域は使用できません。

以降の項では、チェンジ・データ・キャプチャの各モードで必要とされるパブリッシャの設定方法について説明します。

注意:
パブリッシャをSQL DROP USER CASCADE文で削除すると、そのパブリッシャが所有していたチェンジ・データ・キャプチャのすべてのオブジェクト(他のパブリッシャが所有するチェンジ・データ・キャプチャのオブジェクトを含むものは除く)が削除されます。たとえば、チェンジ・テーブルCDCPUB2.SALES_CTが含まれるチェンジ・セットCDCPUB1_SETをパブリッシャCDCPUB1が所有しているものと仮定します。ここで、CDCPUB1を削除するDROP USER CASCADE文を発行しても、CDCPUB1_SETチェンジ・セットは削除されません。ただし、このチェンジ・セットに含まれるすべてのチェンジ・テーブルが削除された後であれば、パブリッシャはDBMS_CDC_PUBLISH.DROP_CHANGE_SETサブプログラムでCDCPUB1_SETチェンジ・セットを削除できます。

パブリッシャに対する権限およびロールの付与

使用するチェンジ・データ・キャプチャのモードにかかわらず、ステージング・データベースのパブリッシャは、次に示す権限およびロールを付与されている必要があります。

- EXECUTE_CATALOG_ROLE権限

- SELECT_CATALOG_ROLE権限

- CREATE TABLE権限およびCREATE SESSION権限

- DBMS_CDC_PUBLISHパッケージのEXECUTE

非同期HotLog、分散HotLog、およびAutoLogの公開では、ステージング・データベースのパブリッシャは、Oracle Streams管理者として構成されている必要があり、CREATE SEQUENCE権限も付与されている必要があります。Oracle Streams管理者の構成に関する詳細は、『Oracle Streams概要および管理』を参照してください。

- CREATE SEQUENCE権限が付与されている

- DBAロールが付与されている

- ステージング・データベースのDBAが発行したDBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGEサブプログラムに指定されているGRANTEEである

非同期分散HotLogの公開では、ソース・データベースのパブリッシャは、DBAロールが付与されており、DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGEサブプログラムで指定されている権限受領者である必要があります。

パブリッシャのデフォルト表領域の作成

ステージング・データベース上にパブリッシャのアカウントを作成する際は、DBAがそのパブリッシャのデフォルト表領域を指定し、パブリッシャは、作成するすべてのチェンジ・テーブル用としてこの表領域を使用することをお薦めします。

パスワード・ファイルおよびREMOTE_LOGIN_PASSWORDFILEパラメータの設定

パスワード・ファイルが生成済であることを確認する必要があります。このファイルは、Database Configuration Assistantを使用するときに自動的に生成されます。

関連項目:

- Oracle Database管理者ガイド

- パスワードの設定の詳細は、「Database Configuration Assistantの考慮事項」を参照してください。

データをキャプチャするモードの決定

変更データをキャプチャするモードの決定は、次の要因によって影響されます。

- ステージング・データベースがソース・データベースとリモートかどうか。

- 変更がソース・データベースに発生してから、チェンジ・データ・キャプチャでキャプチャされるまでの待機時間の許容範囲。一般に、待機時間は表17-4に示す同期から非同期AutoLogアーカイブにかけて長くなります。

- ソース・データベース・トランザクションに対するパフォーマンスの影響およびデータベース全体のパフォーマンス。

- ソース・データベースとステージング・データベースが、同じオペレーティング・システムまたはOracleデータベースの同じリリースを使用して同じハードウェア上で動作するかどうか。

表17-4に、モードの決定に影響を与えるこれらの要因の概要を示します。

表17-4 チェンジ・データ・キャプチャのモードの選択に影響を与える要因

モード

ステージング・データベースの場所と、ハードウェアおよびソフトウェア

キャプチャ・メカニズム

ソース・データベースのパフォーマンスへの影響

同期

場所はソース・データベースと同じである必要があり、ハードウェア、オペレーティング・システムおよびOracleデータベースのリリースはソース・システムと同じである必要があります。

変更データは、反映する同じトランザクションの一部として自動的にコミットされます。

変更データのキャプチャを実行するため、ソース・データベース・トランザクションのオーバーヘッドが増加します。

非同期HotLog

場所はソース・データベースと同じである必要があり、ハードウェア、オペレーティング・システムおよびOracleデータベースのリリースはソース・システムと同じである必要があります。

変更データは、現行のオンラインREDOログ・ファイルからキャプチャされます。チェンジ・セットは、トランザクションが新たにコミットされると自動的に移入されます。

サプリメンタル・ロギングを実行するため、ソース・データベース・トランザクションに対する影響は最小です。変更データのキャプチャを実行すると、ソース・データベースのオーバーヘッドが増加します。

非同期分散HotLog

場所は、ソース・データベースとリモートです。ハードウェア、オペレーティング・システムおよびOracleデータベースのリリースは、ソース・システムと同じである必要はありません。

変更データは、現行のオンラインREDOログ・ファイルからキャプチャされます。チェンジ・セットは、新たにコミットされたトランザクションがステージング・データベースに届くと自動的に移入されます。

サプリメンタル・ロギングを実行するため、ソース・データベース・トランザクションに対する影響は最小です。

オンラインREDOログ・ファイルのマイニング時には、ソース・データベースに多少のオーバーヘッドが生じます。

非同期AutoLogオンライン

場所は、ソース・データベースとリモートです。ハードウェア、オペレーティング・システムおよびOracleデータベースのリリースは、ソース・システムと同じです。

変更データは、スタンバイREDOログ・ファイルからキャプチャされます。チェンジ・セットは、新たにコミットされたトランザクションがステージング・データベースに届くと自動的に移入されます。

サプリメンタル・ロギングを実行するため、ソース・データベース・トランザクションに対する影響は最小です。

REDO転送サービスのソース・データベースのオーバーヘッドは最小です。

非同期AutoLogアーカイブ

場所は、ソース・データベースとリモートです。ハードウェア、オペレーティング・システムおよびOracleデータベースのリリースは、ソース・システムと同じです。

変更データは、アーカイブREDOログ・ファイルからキャプチャされます。チェンジ・セットは、アーカイブREDOログ・ファイルがステージング・データベースに届くと自動的に移入されます。

サプリメンタル・ロギングを実行するため、ソース・データベース・トランザクションに対する影響は最小です。

REDO転送サービスのソース・データベースのオーバーヘッドは最小です。

チェンジ・データ・キャプチャの公開用初期化パラメータの設定

初期化パラメータは、チェンジ・データ・キャプチャを正常に行うために、ソース・データベースかステージング・データベース、またはその両方で設定する必要があります。どのパラメータを設定するかは、チェンジ・データ・キャプチャが変更データを公開するモード、および、ソース・データベースかステージング・データベースのどちらでパラメータを設定しているかに依存します。

次の項では、チェンジ・データ・キャプチャの各モードにおける、データベース初期化パラメータの設定について説明します。DBAが、現行の設定に値を追加する必要がある場合もあります。DBAは、SQL SHOW PARAMETERS文を使用して現行の設定値を参照できます。

これらのデータベース初期化パラメータの一般情報は『Oracle Databaseリファレンス』を、非同期の公開用のデータベース初期化パラメータ設定の詳細は『Oracle Streams概要および管理』を参照してください。

同期公開用初期化パラメータ

JAVA_POOL_SIZEパラメータを次のように設定します。

JAVA_POOL_SIZE = 50000000

非同期HotLogの公開用初期化パラメータ

表17-5に、ソース・データベースの初期化パラメータおよび非同期HotLogの公開用の推奨設定を示します。

表17-5 非同期HotLogの公開用のソース・データベース初期化パラメータ

パラメータ

推奨値

COMPATIBLE

11.0

JAVA_POOL_SIZE

50000000

JOB_QUEUE_PROCESSES

(現行値) + 2

PARALLEL_MAX_SERVERS

(現行値) + (5 * (計画しているチェンジ・セットの数))

PROCESSES

(現行値) + (7 * (計画しているチェンジ・セットの数))

SESSIONS

(現行値) + (2 * (計画しているチェンジ・セットの数))

STREAMS_POOL_SIZE

- STREAMS_POOL_SIZEパラメータの現行値が50MB以上の場合、このパラメータは次のように設定します。
(現行値) + ((計画しているチェンジ・セットの数) * (21MB))

- STREAMS_POOL_SIZEパラメータの現行値が50MBより小さい場合、このパラメータの値は次のように設定します。
50MB + ((計画しているチェンジ・セットの数) * (21MB))

動的な変更の際にSTREAMS_POOL_SIZEパラメータがどのように適用されるかについては、『Oracle Streams概要および管理』を参照してください。

UNDO_RETENTION

3600

非同期分散HotLogの公開用初期化パラメータ

表17-6に、ソース・データベースがOracle Databaseリリース10.1.0、10.2.0、11.1.0または11.2.0の場合の、ソース・データベースの初期化パラメータおよび非同期分散HotLogの公開用の推奨設定を示します。

表17-7に、ソース・データベースがOracle Databaseリリース9.2の場合の、ソース・データベースの初期化パラメータおよび非同期分散HotLogの公開用の推奨設定を示します。

表17-8に、ステージング・データベースの初期化パラメータおよび非同期分散HotLogの公開用の推奨設定を示します。これらは、ソース・データベースに使用されているOracleデータベースのリリースにかかわらず同じです。

表17-6 非同期分散HotLogの公開用のソース・データベース(10.1.0、10.2.0、11.1.0、11.2.0)初期化パラメータ

パラメータ

推奨値

COMPATIBLE

11.0(ソースOracleデータベースのリリースによって異なる)

GLOBAL_NAMES

TRUE

JOB_QUEUE_PROCESSES

(現行値) + 2

OPEN_LINKS

4または計画している分散HotLogチェンジ・ソース数のいずれか大きい方の値

PARALLEL_MAX_SERVERS

(現行値) + (3 * (計画しているチェンジ・ソースの数))

PROCESSES

(現行値) + (4 * (計画しているチェンジ・ソースの数))

SESSIONS

(現行値) + (計画しているチェンジ・ソースの数)

STREAMS_POOL_SIZE

- STREAMS_POOL_SIZEパラメータの現行値が50MB以上の場合、このパラメータは次のように設定します。
(現行値) + ((計画しているチェンジ・ソースの数) * (20MB))

- STREAMS_POOL_SIZEパラメータの現行値が50MBより小さい場合、このパラメータの値は次のように設定します。
50MB + ((計画しているチェンジ・セットの数) * (20MB))

動的な変更の際にSTREAMS_POOL_SIZEパラメータがどのように適用されるかについては、『Oracle Streams概要および管理』を参照してください。

UNDO_RETENTION

3600

表17-7 非同期分散HotLogの公開用のソース・データベース(9.2)初期化パラメータ

パラメータ

推奨値

COMPATIBLE

9.2.0

GLOBAL_NAMES

TRUE

JOB_QUEUE_PROCESSES

(現行値) + 2

LOG_PARALLELISM

1

LOGMNR_MAX_PERSISTENT_SESSIONS

計画しているチェンジ・ソースの数と同じ値

OPEN_LINKS

4または計画している分散HotLogチェンジ・ソース数のいずれか大きい方の値

PARALLEL_MAX_SERVERS

(現行値) + (3 * (計画しているチェンジ・ソースの数))

PROCESSES

(現行値) + (4 * (計画しているチェンジ・ソースの数))

SESSIONS

(現行値) + (計画しているチェンジ・ソースの数)

SHARED_POOL_SIZE

- SHARED_POOL_SIZEパラメータの現行値が50MB以上の場合、このパラメータは次のように設定します。
(現行値) + ((計画しているチェンジ・ソースの数) * (20MB))

- SHARED_POOL_SIZEパラメータの現行値が50MBより小さい場合、このパラメータの値は次のように設定します。
50MB * ((計画しているチェンジ・ソースの数) * (20MB))

UNDO_RETENTION

3600

表17-8 非同期分散HotLogの公開用のステージング・データベース(11.2.0)初期化パラメータ

パラメータ

推奨値

COMPATIBLE

11.0

GLOBAL_NAMES

TRUE

JAVA_POOL_SIZE

50000000

OPEN_LINKS

4または計画している分散HotLogチェンジ・セット数のいずれか大きい方の値

PARALLEL_MAX_SERVERS

(現行値) + (2 * (計画しているチェンジ・セットの数))

PROCESSES

(現行値) + (3 * (計画しているチェンジ・セットの数))

SESSIONS

(現行値) + (計画しているチェンジ・セットの数)

STREAMS_POOL_SIZE

- STREAMS_POOL_SIZEパラメータの現行値が50MB以上の場合、このパラメータは次のように設定します。
(現行値) * ((計画しているチェンジ・セットの数) * (11MB))

- STREAMS_POOL_SIZEパラメータの現行値が50MBより小さい場合、このパラメータの値は次のように設定します。
50MB + ((計画しているチェンジ・セットの数) * (11MB))

動的な変更の際にSTREAMS_POOL_SIZEパラメータがどのように適用されるかについては、『Oracle Streams概要および管理』を参照してください。

非同期AutoLogの公開用初期化パラメータ

表17-9に、データベース初期化パラメータとソース・データベースの非同期AutoLogの公開用推奨設定を、表17-10に、データベース初期化パラメータとステージング・データベースの非同期AutoLogの公開用推奨設定を示します。

表17-9 非同期AutoLogの公開用のソース・データベース初期化パラメータ

パラメータ

推奨値

COMPATIBLE

11.0

LOG_ARCHIVE_DEST_1脚注1

自身のアーカイブREDOログ・ファイルが保存されるソース・データベース上のディレクトリ指定。

LOG_ARCHIVE_DEST_2脚注1

このパラメータには、SERVICE、ARCHかLGWR ASYNC、OPTIONAL、NOREGISTER、REOPEN属性を指定する必要があります。この属性によって、REDOログ・ファイルがソース・データベースからステージング・データベースにコピーされるようREDO転送サービスが構成されます。また、このパラメータには、AutoLogオプションに応じて、VALID_FORまたはTEMPLATE属性を指定する必要があります。これらの属性は次のような設定となります。

- SERVICE: ステージング・データベースのネットワーク名を指定します。

- ARCHまたはLGWR ASYNC:
AutoLogオンライン・オプションを使用するには、LGWR ASYNCを指定します。LGWR ASYNCは、REDOがソース・データベースで生成されると、ログ・ライター・プロセス(LGWR)によって、REDOデータがステージング・データベースに非同期式にコピーされるように指定します。コピーしたREDOデータがチェンジ・データ・キャプチャで使用できるようになるのは、ソース・データベースのトランザクションのコミット後です。AutoLogアーカイブ・オプションを使用するには、ARCHまたはLGWR ASYNCを指定します。ARCHは、ソース・データベースのログ・スイッチの発生後に、アーカイバ・プロセス(ARCn)によって、REDOログ・ファイルがステージング・データベースにコピーされるように指定します。LGWR ASYNCは、REDOがソース・データベースで生成されると、ログ・ライター・プロセス(LGWR)によって、REDOデータがステージング・データベースに非同期式にコピーされるように指定します。ARCHおよびLGWR ASYNCのどちらの場合でも、コピーしたREDOデータがチェンジ・データ・キャプチャで使用できるようになるのは、AutoLogアーカイブ・オプション使用時におけるソース・データベースのログ・スイッチの発生後です。

- OPTIONALは、ソース・データベースの対応するオンラインREDOログが上書きされることが可能となるまでは、ステージング・データベースへのREDOログ・ファイルのコピーが成功する必要がないことを指定します。これは、ステージング・データベースへの転送失敗に起因するソース・データベースでの操作の停止を回避するために必要です。元のREDOログ・ファイルはアーカイブまたはバックアップされた形式で、必要であればソース・データベースで使用できる状態で残ります。

- NOREGISTER: ステージング・データベースの場所がステージング・データベースの制御ファイルに記録されないように指定します。

- REOPEN: ステージング・データベースへのアクセスが失敗した場合、再度アクセスする前に、ログ・ライター・プロセス(LGWR)またはアーカイバ・プロセス(ARCn)が待機する必要のある最小秒数を指定します。

- VALID_FOR: AutoLogオンライン・オプション使用時に、ソース・データベースのオンラインREDOログから、ステージング・データベースのスタンバイREDOログにREDOデータをコピーできるようにするには、VALID_FORを(ONLINE_LOGFILE, PRIMARY_ROLE)または(ONLINE_LOGFILE, ALL_ROLES)のいずれかに設定します。

- TEMPLATE AutoLogアーカイブ・オプション使用時に、TEMPLATEを指定して、ステージング・データベースにコピーされるアーカイブREDOログ・ファイルに使用されるファイル名のフォーマット・テンプレートおよびディレクトリ指定を定義します。脚注2

LOG_ARCHIVE_DEST_STATE_1脚注1

ENABLE

REDO転送サービスでアーカイブREDOログ・ファイルをこの宛先に転送できるよう指定します。

LOG_ARCHIVE_DEST_STATE_2脚注1

ENABLE

REDO転送サービスでREDOログ・ファイルをこの宛先に転送できるよう指定します。

LOG_ARCHIVE_FORMAT脚注2

"arch_%s_%t_%r.dbf"

REDOログ・ファイルをアーカイブする際の、デフォルトのファイル名のフォーマット・テンプレートを指定します。脚注2 文字列値(arch)およびファイル名の拡張子(.dbf)は、ここで示しているものと同じである必要はありません。

REMOTE_ARCHIVE_ENABLE

TRUE

このソース・データベースでREDOログ・ファイルをリモートの宛先に送信できるよう指定します。

脚注1このパラメータの整数値は、1から10の間の任意の値を指定できます。このマニュアルでは、値1および2を使用します。各LOG_ARCHIVE_DEST_nパラメータに対して、対応するLOG_ARCHIVE_DEST_STATE_nパラメータ(nは同じ数値)を指定する必要があります。

脚注2フォーマット・テンプレートでは、%tがスレッド番号、%sが順序番号、%rがリセットログIDに対応します。これらを組み合せることにより、コピーされたREDOログ・ファイルに一意の名前が割り当てられます。どの要素も必須ですが、順番とフォーマットは柔軟に変更できます。

表17-10 非同期AutoLogの公開用のステージング・データベース初期化パラメータ

パラメータ

推奨値

COMPATIBLE

11.2.0

GLOBAL_NAMES

TRUE

JAVA_POOL_SIZE

50000000

LOG_ARCHIVE_DEST_1脚注1

自身のアーカイブREDOログ・ファイルが保存されるステージング・データベース上のディレクトリ指定。ステージング・データベースにAutoLogオンライン・チェンジ・ソースが含まれる場合、次の属性を指定する必要があります。

- LOCATIONは、ステージング・データベースの自身のアーカイブREDOログ・ファイルに一意のディレクトリ・パス名を指定します。

- オンラインREDOログ・ファイルをローカルでアーカイブできるようにするには、VALID_FORを(ONLINE_LOGFILE, PRIMARY_ROLE)または(ONLINE_LOGFILE, ALL_ROLES)のいずれかに設定します。

LOG_ARCHIVE_DEST_2脚注1

ステージング・データベースにAutoLogオンライン・チェンジ・ソースが含まれる場合、これは、ソース・データベースから変更データを受け取る、ステージング・データベース上のスタンバイREDOログ・ファイルを指定します。これらのスタンバイREDOログ・ファイルには一意の場所を指定することが非常に重要です。一意の場所を指定することで、ステージング・データベースの自身のアーカイブ・ログ・ファイルがこれらのスタンバイREDOログ・ファイルによって上書きされないようにするためです。

- LOCATIONは、ステージング・データベースのスタンバイREDOログ・ファイルに一意のディレクトリ・パス名を指定します。

- MANDATORYは、スタンバイREDOログ・ファイルが、上書きされる前に正常にアーカイブされるように指定します。

- ステージング・データベースで、ソース・データベースから変更データを受け取り、その変更データをステージング・データベースのスタンバイ・ログ・ファイルに書き込むようにするには、VALID_FORを(STANDBY_LOGFILE, PRIMARY_ROLE)または(STANDBY_LOGFILE, ALL_ROLES)のいずれかに設定します。

LOG_ARCHIVE_DEST_STATE_1脚注1

ENABLE

REDO転送サービスでアーカイブREDOログ・ファイルをこの宛先に転送できるよう指定します。

LOG_ARCHIVE_DEST_STATE_2脚注1

ENABLE

REDO転送サービスでREDOログ・ファイルをこの宛先に転送できるよう指定します。

LOG_ARCHIVE_FORMAT脚注2

"arch_%s_%t_%r.dbf"

REDOログ・ファイルをアーカイブする際に、デフォルトのファイル名のフォーマット・テンプレートを指定します。脚注2文字列値(arch)およびファイル名の拡張子(.dbf)は、ここで示しているものと同じである必要はありません。

JOB_QUEUE_PROCESSES

2

PARALLEL_MAX_SERVERS

(現行値) + (5 * (計画しているチェンジ・セットの数))

PROCESSES

(現行値) + (7 * (計画しているチェンジ・セットの数))

REMOTE_ARCHIVE_ENABLE

TRUE

このステージング・データベースで、アーカイブREDOログ・ファイルをリモートで受け取ることができるよう指定します。

SESSIONS

(現行値) + (2 * (計画しているチェンジ・セットの数))

STREAMS_POOL_SIZE

- STREAMS_POOL_SIZEパラメータの現行値が50MB以上の場合、このパラメータは次のように設定します。
(現行値) + ((計画しているチェンジ・セットの数) * (21MB))

- STREAMS_POOL_SIZEパラメータの現行値が50MBより小さい場合、このパラメータの値は次のように設定します。
50MB + ((計画しているチェンジ・セットの数) * (21MB))

動的な変更の際にSTREAMS_POOL_SIZEパラメータがどのように適用されるかについては、『Oracle Streams概要および管理』を参照してください。

UNDO_RETENTION

3600

脚注1このパラメータの整数値は、1から10の間の任意の値を指定できます。このマニュアルでは、値1および2を使用します。各LOG_ARCHIVE_DEST_nパラメータに対して、対応するLOG_ARCHIVE_DEST_STATE_nパラメータ(nは同じ数値)を指定する必要があります。

脚注2フォーマット・テンプレートでは、%tがスレッド番号、%sが順序番号、%rがリセットログIDに対応します。これらを組み合せることにより、コピーされたREDOログ・ファイルに一意の名前が割り当てられます。どの要素も必須ですが、順番とフォーマットは柔軟に変更できます。

Oracle Streamsの値変更時の初期化パラメータ値の調整

非同期チェンジ・データ・キャプチャでは、各チェンジ・セットについて、Oracle Streams構成を使用します。このStreams構成は、Streamsの取得プロセスと適用プロセス、キューおよびキュー表で構成されます。各Streams構成では、追加のプロセス、パラレル実行サーバーおよびメモリーが使用されます。Streamsアーキテクチャの詳細は、『Oracle Streams概要および管理』を参照してください。

構成になんらかの変更があった場合は、初期化パラメータの調整が必要となることがあります。詳細は、表17-10「非同期AutoLogの公開用のステージング・データベース初期化パラメータ」を参照してください。

CDC環境に対する変更のトラッキング

チェンジ・テーブルでパーティションのメンテナンス操作、ダイレクト・パス・ロード、DDL操作が発生したときに、これらを追跡することが可能です。これを実行するには、DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャのddl_markersフラグをセットします。

変更データの公開

次の項では、様々なタイプの公開を実行する手順を提供します。

- 同期公開の実行

- 非同期HotLogの公開の実行

- 非同期分散HotLogの公開の実行

- 非同期AutoLogの公開の実行

同期公開の実行

同期チェンジ・データ・キャプチャに対しては、パブリッシャは事前定義のチェンジ・ソースSYNC_SOURCEを使用する必要があります。パブリッシャは、新しいチェンジ・セットを定義したり、事前定義のチェンジ・セットSYNC_SETを使用したりできます。パブリッシャは、SYSまたはSYSTEMが所有するソース・テーブル上にチェンジ・テーブルを作成してはいけません。こうすると、トリガーが起動されず、変更がキャプチャされないためです。

この例では、チェンジ・セットの作成方法を示します。パブリッシャが事前定義のSYNC_SETを使用する場合は、手順3をスキップして、残りの手順でチェンジ・セット名としてSYNC_SETを指定してください。

この例では、パブリッシャとソース・データベースDBAは2人の異なるユーザーとします。

同期チェンジ・データ・キャプチャでは、ソース・データベースとステージング・データベースは同じであることに注意してください。

手順1   ソース・データベースDBA: JAVA_POOL_SIZEパラメータの設定

ソース・データベースDBAは、「チェンジ・データ・キャプチャの公開用初期化パラメータの設定」で説明しているように、データベース初期化パラメータを設定します。

java_pool_size = 50000000

手順2   ソース・データベースDBA: パブリッシャに対する権限の作成および付与

ソース・データベースDBAは、チェンジ・データ・キャプチャのパブリッシャとなるユーザー(cdcpubなど)を作成し、必要な権限をそのパブリッシャに付与することで、チェンジ・データ・キャプチャのチェンジ・セットおよびチェンジ・テーブルをソース・データベース上に作成するために必要な操作をそのパブリッシャが実行できるようにします(「パブリッシャとなるユーザーの作成」を参照)。この例では、表領域ts_cdcpubがすでに作成済であるものとします。

CREATE USER cdcpub IDENTIFIED EXTERNALLY DEFAULT TABLESPACE ts_cdcpub
QUOTA UNLIMITED ON SYSTEM
QUOTA UNLIMITED ON SYSAUX;
GRANT CREATE SESSION TO cdcpub;
GRANT CREATE TABLE TO cdcpub;
GRANT CREATE TABLESPACE TO cdcpub;
GRANT CREATE JOB TO cdcpub;
GRANT UNLIMITED TABLESPACE TO cdcpub;
GRANT SELECT_CATALOG_ROLE TO cdcpub;
GRANT EXECUTE_CATALOG_ROLE TO cdcpub;
GRANT ALL ON sh.sales TO cdcpub;
GRANT ALL ON sh.products TO cdcpub;
GRANT EXECUTE ON DBMS_CDC_PUBLISH TO cdcpub;

手順3   ステージング・データベースのパブリッシャ: チェンジ・セットの作成

パブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_CHANGE_SETプロシージャを使用してチェンジ・セットを作成します。

次の例では、CHICAGO_DAILYというチェンジ・セットの作成方法を示します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_SET(
change_set_name    => 'CHICAGO_DAILY',
description        => 'Change set for sales history info',
change_source_name => 'SYNC_SOURCE');
END;
/

チェンジ・セットは、事前定義のチェンジ・ソースSYNC_SOURCEから変更をキャプチャします。begin_dateおよびend_dateパラメータは同期チェンジ・セットに指定できないので、変更データのキャプチャは、変更データが最初に使用可能となった時点から始まり、無期限に続きます。

手順4   ステージング・データベースのパブリッシャ: チェンジ・テーブルの作成

パブリッシャは、DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャを使用してチェンジ・テーブルを作成します。

パブリッシャは、DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャのoptions_stringフィールドを設定して、チェンジ・テーブルの物理プロパティと表領域プロパティを厳密に制御できます。options_stringフィールドには、CREATE TABLE文で使用可能な、パーティション化以外の任意のオプションを指定できます。

次の例では、ソース・テーブルに発生した変更をキャプチャするチェンジ・テーブルを作成します。例では、ソース・テーブルとして、サンプル・スキーマ表sh.productsを使用します。パブリッシャがすでにTS_CHICAGO_DAILY表領域を作成済であるものとします。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLE(

owner             => 'cdcpub',
change_table_name => 'products_ct',
change_set_name   => 'CHICAGO_DAILY',
source_schema     => 'SH',
source_table      => 'PRODUCTS',
column_type_list  => 'PROD_ID NUMBER(6),
PROD_NAME VARCHAR2(50),
PROD_LIST_PRICE NUMBER(8,2)',
capture_values    => 'both',
rs_id             => 'y',
row_id            => 'n',
user_id           => 'n',
timestamp         => 'n',
object_id         => 'n',
source_colmap     => 'y',
target_colmap     => 'y',
options_string    => 'TABLESPACE TS_CHICAGO_DAILY');
END;
/

この文では、チェンジ・セットCHICAGO_DAILY内にproducts_ctという名前のチェンジ・テーブルを作成します。column_type_listパラメータには、このチェンジ・テーブルによりキャプチャされる列を指定します。source_schemaおよびsource_tableパラメータは、ソース・データベースにあるスキーマおよびソース・テーブルを特定します。

例におけるcapture_values設定は、更新操作で変更された各行について、変更データが2つの別個の行を持つことを示します。1つ目の行には更新前の行値が含まれ、2つ目の行には更新後の行値が含まれます。

詳細は、「チェンジ・テーブルの管理」を参照してください。

手順5   ステージング・データベースのパブリッシャ: サブスクライバへのアクセス権の付与

パブリッシャは、ユーザーおよびロールに対してチェンジ・テーブルへのSELECT権限の付与および取消しを行うことで、サブスクライバの変更データへのアクセスを制御します。パブリッシャは、特定のチェンジ・テーブルへのアクセス権を付与します。この手順を行わないと、サブスクライバはどのような変更データにもアクセスできません。この例では、ユーザーsubscriber1がすでに存在しているものとします。

GRANT SELECT ON cdcpub.products_ct TO subscriber1;

これで、チェンジ・データ・キャプチャの同期システムが設定され、subscriber1がサブスクリプションを作成する準備ができました。

非同期HotLogの公開の実行

チェンジ・データ・キャプチャはOracle Streamsのローカル・キャプチャを使用して、非同期HotLogの公開を実行します。Oracle Streamsのローカル・キャプチャの詳細は、『Oracle Streams概要および管理』を参照してください。

HotLogのチェンジ・データ・キャプチャでは、パブリッシャは、事前定義のチェンジ・ソースHOTLOG_SOURCEを使用し、変更を格納するチェンジ・テーブルとチェンジ・セットを作成する必要があります。ステージング・データベースは常にソース・データベースです。この例では、パブリッシャとソース・データベースDBAは2人の異なるユーザーとします。

非同期のHotLogチェンジ・データ・キャプチャでは、ソース・データベースとステージング・データベースは同じであることに注意してください。

次の手順では、非同期HotLogの公開のための、REDOロギング、Oracle Streamsおよびチェンジ・データ・キャプチャを設定します。

手順1   ソース・データベースDBA: データベース初期化パラメータの設定

ソース・データベースDBAは、「チェンジ・データ・キャプチャの公開用初期化パラメータの設定」で説明しているように、データベース初期化パラメータを設定します。この例では、1つのチェンジ・セットが定義されます。STREAMS_POOL_SIZEパラメータの現行値は50MBです。

compatible = 11.0
java_pool_size = 50000000
job_queue_processes = 2

parallel_max_servers = <current value> + 5
processes = <current value> + 7
sessions = <current value> + 2
streams_pool_size = <current value> + 21 MB
undo_retention = 3600

手順2   ソース・データベースDBA: ソース・データベースの変更

ソース・データベースDBAは、次の3つのタスクを実行します。2つ目は必須です。1つ目と3つ目はオプションですが、実行することをお薦めします。データベースは現在、ARCHIVELOGモードで実行されているものとします。

1. データベースをFORCE LOGGINGロギング・モードに設定し、非同期チェンジ・データ・キャプチャではキャプチャできない、ソース・データベースでのロギングされないダイレクト書込み操作を回避します。

ALTER DATABASE FORCE LOGGING;

ロギングは、表領域レベルまたは表レベルでも実行可能です。

2. サプリメンタル・ロギングを有効化します。サプリメンタル・ロギングによって、UPDATE操作を実行するとすぐに、追加の列データがREDOログ・ファイルに配置されます。最低限、どのようなチェンジ・データ・キャプチャのソース・データベースに対しても、データベース・レベルの最小サプリメンタル・ロギングを有効化する必要があります。

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

3. ソース・テーブルでキャプチャされるすべての列に対して、無条件のログ・グループを作成します。変更されない、無条件のログ・グループにないソース・テーブルの列は、実際のソース・テーブルの値を反映せずに、チェンジ・テーブルにおいてNULLとなります。この例では、sh.products表の行のみをキャプチャします。ソース・データベースDBAは、チェンジ・テーブルが作成される各ソース・テーブルについて、この手順を繰り返します。

ALTER TABLE sh.products
ADD SUPPLEMENTAL LOG GROUP log_group_products
(PROD_ID, PROD_NAME, PROD_LIST_PRICE) ALWAYS;

行の列が更新されたらすぐにその行のすべての列値をキャプチャする場合、ALTER TABLE文に各列を1つずつリストするのではなく、次の文を使用できます。ただし、すべての列を必要としない場合、この形式のALTER TABLE文は使用しないでください。すべての列のロギングを実行すると、選択した列のみのロギングよりも大きなオーバーヘッドが生じます。

ALTER TABLE sh.products ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

ARCHIVELOGモードでのデータベースの運用およびFORCE LOGGINGモードについては、『Oracle Database管理者ガイド』を参照してください。サプリメンタル・ロギングの詳細は、「非同期チェンジ・データ・キャプチャとサプリメンタル・ロギング」および『Oracle Databaseユーティリティ』を参照してください。

手順3   ソース・データベースDBA: パブリッシャに対する権限の作成および付与

ソース・データベースDBAは、チェンジ・データ・キャプチャのパブリッシャとなるユーザー(cdcpubなど)を作成し、必要な権限をそのパブリッシャに付与することで、チェンジ・データ・キャプチャのチェンジ・セットおよびチェンジ・テーブルをソース・データベース上に作成するために必要な基底のOracle Streams操作をそのパブリッシャが実行できるようにします(「パブリッシャとなるユーザーの作成」を参照)。この例では、表領域ts_cdcpubがすでに作成済であるものとします。

CREATE USER cdcpub IDENTIFIED EXTERNALLY DEFAULT TABLESPACE ts_cdcpub
QUOTA UNLIMITED ON SYSTEM
QUOTA UNLIMITED ON SYSAUX;
GRANT CREATE SESSION TO cdcpub;
GRANT CREATE TABLE TO cdcpub;
GRANT CREATE TABLESPACE TO cdcpub;
GRANT UNLIMITED TABLESPACE TO cdcpub;
GRANT SELECT_CATALOG_ROLE TO cdcpub;
GRANT EXECUTE_CATALOG_ROLE TO cdcpub;
GRANT CREATE SEQUENCE TO cdcpub;
GRANT DBA TO cdcpub;
GRANT EXECUTE on DBMS_CDC_PUBLISH TO cdcpub;

EXECUTE DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGE(GRANTEE => 'cdcpub');

HotLogチェンジ・データ・キャプチャでは、ソース・データベースとステージング・データベースは同じデータベースであることに注意してください。

手順4   ソース・データベースDBA: ソース・テーブルの準備

ソース・データベースDBAは、各ソース・テーブルをインスタンス化することによって、非同期チェンジ・データ・キャプチャのソース・データベース上にソース・テーブルを準備する必要があります。各ソース・テーブルをインスタンス化すると、基底のOracle Streams環境で、各ソース・テーブルの変更のキャプチャに必要な情報が記録されます。ソース・テーブルの構造および列データ型は、チェンジ・データ・キャプチャでサポートされる必要があります。詳細は、「非同期チェンジ・データ・キャプチャでサポートされるデータ型と表構造」を参照してください。

BEGIN
DBMS_CAPTURE_ADM.PREPARE_TABLE_INSTANTIATION(TABLE_NAME => 'sh.products');
END;
/

手順5   ステージング・データベースのパブリッシャ: チェンジ・セットの作成

パブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_CHANGE_SETプロシージャを使用してチェンジ・セットを作成します。チェンジ・データ・キャプチャによってチェンジ・セットが作成される際に、関連するOracle Streamsの取得プロセスと適用プロセスも作成される(開始はされない)ことに注意してください。

次の例では、今日から発生する変更をキャプチャし、5日後に変更データのキャプチャを終了するCHICAGO_DAILYというチェンジ・セットを作成します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_SET(
change_set_name => 'CHICAGO_DAILY',
description => 'Change set for product info',
change_source_name => 'HOTLOG_SOURCE',
stop_on_ddl => 'y',
begin_date => sysdate,
end_date => sysdate+5);
END;
/

チェンジ・セットは、事前定義のチェンジ・ソースHOTLOG_SOURCEから変更をキャプチャします。

手順6   ステージング・データベースのパブリッシャ: ソース・テーブルに対する変更を格納するチェンジ・テーブルの作成

パブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャを使用してチェンジ・テーブルを作成します。

パブリッシャは、公開される各ソース・テーブルについて、1つ以上のチェンジ・テーブルを作成し、含める列、およびキャプチャする変更データのビフォア・イメージおよびアフター・イメージの組合せを指定します。

次の例では、ソース・データベースのソース・テーブルに加えられた変更をキャプチャするチェンジ・テーブルをステージング・データベースに作成します。例では、ソース・テーブルとして、サンプル表sh.productsを使用します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLE(
owner              => 'cdcpub',
change_table_name  => 'products_ct',
change_set_name    => 'CHICAGO_DAILY',
source_schema      => 'SH',
source_table       => 'PRODUCTS',
column_type_list   => 'PROD_ID NUMBER(6), PROD_NAME VARCHAR2(50),
PROD_LIST_PRICE NUMBER(8,2)',
capture_values     => 'both',
rs_id              => 'y',
row_id             => 'n',
user_id            => 'n',
timestamp          => 'n',
object_id          => 'n',
source_colmap      => 'n',
target_colmap      => 'y',
options_string     => 'TABLESPACE TS_CHICAGO_DAILY');
END;
/

この文では、チェンジ・セットCHICAGO_DAILY内にproducts_ctという名前のチェンジ・テーブルを作成します。column_type_listパラメータには、このチェンジ・テーブルによりキャプチャされる列を指定します。source_schemaおよびsource_tableパラメータには、ソース・データベースにあるスキーマおよびソース・テーブルを指定します。

この文におけるcapture_values設定は、更新操作で変更された各行について、変更データが2つの別個の行を持つことを示します。1つ目の行には更新前の行値が含まれ、2つ目の行には更新後の行値が含まれます。

この文のoptions_stringパラメータは、チェンジ・テーブルの表領域を指定します。この例では、パブリッシャがすでにTS_CHICAGO_DAILY表領域を作成済であるものとします。

詳細は、「チェンジ・テーブルの管理」を参照してください。

手順7   ステージング・データベースのパブリッシャ: チェンジ・セットの有効化

非同期チェンジ・セットは作成時には常に無効化されているため、パブリッシャがチェンジ・セットを変更して有効化する必要があります。チェンジ・セットを有効化すると、Oracle Streamsの取得プロセスおよび適用プロセスが開始されます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET(
change_set_name => 'CHICAGO_DAILY',
enable_capture => 'y');
END;
/

手順8   ステージング・データベースのパブリッシャ: サブスクライバへのアクセス権の付与

パブリッシャは、ユーザーおよびロールに対してチェンジ・テーブルへのSELECT権限の付与および取消しを行うことで、サブスクライバの変更データへのアクセスを制御します。パブリッシャは、特定のチェンジ・テーブルへのアクセス権を付与します。この手順を行わないと、サブスクライバは変更データにアクセスできません。この例では、ユーザーsubscriber1がすでに存在しているものとします。

GRANT SELECT ON cdcpub.products_ct TO subscriber1;

これで、チェンジ・データ・キャプチャの非同期HotLogシステムが設定され、subscriber1がサブスクリプションを作成する準備ができました。

非同期分散HotLogの公開の実行

分散HotLogのチェンジ・データ・キャプチャでは、ステージング・データベースはソース・データベースとリモートです。ただし、データベース・リンク、Oracle Streamsおよびチェンジ・データ・キャプチャを設定する手順は、ソース・データベースとステージング・データベースの両方で実行する必要があります。次に、ソース・データベースDBA、ステージング・データベースDBA、および各データベースのパブリッシャが実行する必要があるタスクを示します。

- ソース・データベースDBAは、Oracle Net、データベース初期化パラメータの設定、強制ロギングおよびサプリメンタル・ロギングを有効化するためのソース・データベースの変更、およびソース・データベースのパブリッシャの作成を行います。

- ステージング・データベースDBAは、データベース初期化パラメータの設定およびステージング・データベースのパブリッシャの作成を行います。

- ソース・データベースのパブリッシャは、ソース・データベースからステージング・データベースへのデータベース・リンクの設定を行います。

- ステージング・データベースのパブリッシャは、ステージング・データベースからソース・データベースへのデータベース・リンクの設定、個々のソース・テーブルに加えられる変更を格納するチェンジ・テーブル、チェンジ・セット、チェンジ・ソースの新規作成、および必要な権限のサブスクライバへの付与を行います。

この例では、ソース・データベースDBA、ステージング・データベースDBA、および各データベースのパブリッシャは4人の異なるユーザーとします。

手順1   ソース・データベースDBA: ソース・データベースの準備

ソース・データベースDBAは、次のタスクを実行します。

1. Oracle Netを構成して、ソース・データベースがステージング・データベースと通信できるようにします。Oracle Netについては、『Oracle Database Net Services管理者ガイド』を参照してください。

2. 「チェンジ・データ・キャプチャの公開用初期化パラメータの設定」で説明しているように、ソース・データベース上でデータベース初期化パラメータを設定します。次のコード例では、ソース・データベースがOracle Databaseリリース11.2、計画しているチェンジ・ソースの数が1、およびSTREAMS_POOL_SIZEパラメータの現行値が50MBです。

compatible = 11.0
global_names = true
job_queue_processes = <current value> + 2
open_links = 4

parallel_max_servers = <current value> + 3
processes = <current value> + 4
sessions = <current value> + 1
streams_pool_size = <current value> + 20 MB
undo_retention = 3600

手順2   ステージング・データベースDBA: データベース初期化パラメータの設定

ステージング・データベースDBAは、次のタスクを実行します。

1. 「チェンジ・データ・キャプチャの公開用初期化パラメータの設定」で説明しているように、ステージング・データベース上でデータベース初期化パラメータを設定します。この例では、1つのチェンジ・セットが定義されます。STREAMS_POOL_SIZEパラメータの現行値は50MB以上です。

compatible = 11.0
global_names = true
java_pool_size = 50000000
open_links = 4
job_queue_processes = 2
parallel_max_servers = <current_value> + 2
processes = <current_value> + 3
sessions = <current value> + 1
streams_pool_size = <current_value> + 11 MB
undo_retention = 3600

手順3   ソース・データベースDBA: ソース・データベースの変更

ソース・データベースDBAは、次の3つのタスクを実行します。2つ目は必須です。1つ目と3つ目はオプションですが、実行することをお薦めします。データベースは現在、ARCHIVELOGモードで実行されているものとします。

1. データベースをFORCE LOGGINGロギング・モードに設定し、非同期チェンジ・データ・キャプチャではキャプチャできない、ソース・データベースでのロギングされないダイレクト書込みを回避します。

ALTER DATABASE FORCE LOGGING;

2. サプリメンタル・ロギングを有効化します。サプリメンタル・ロギングによって、更新操作を実行するとすぐに、追加の列データがREDOログ・ファイルに配置されます。

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

3. ソース・テーブルでキャプチャされるすべての列に対して、無条件のログ・グループを作成します。変更されない、無条件のログ・グループにないソース・テーブルの列は、実際のソース・テーブルの値を反映せずに、チェンジ・テーブルにおいてNULLとなります。この例では、sh.products表の行のみをキャプチャします。ソース・データベースDBAは、チェンジ・テーブルが作成される各ソース・テーブルについて、この手順を繰り返します。

ALTER TABLE sh.products
ADD SUPPLEMENTAL LOG GROUP log_group_products
(PROD_ID, PROD_NAME, PROD_LIST_PRICE) ALWAYS;

行の列が更新されたらすぐにその行のすべての列値をキャプチャする場合、ALTER TABLE文に各列を1つずつリストするのではなく、次の文を使用できます。ただし、すべての列を必要としない場合、この形式のALTER TABLE文は使用しないでください。すべての列のロギングを実行すると、選択した列のみのロギングよりも大きなオーバーヘッドが生じます。

ALTER TABLE sh.products ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

関連項目:

- ARCHIVELOGモードでのデータベースの運用については、『Oracle Database管理者ガイド』を参照してください。

- サプリメンタル・ロギングの詳細は、「非同期チェンジ・データ・キャプチャとサプリメンタル・ロギング」を参照してください。

- サプリメンタル・ロギングの詳細は、『Oracle Databaseユーティリティ』を参照してください。

手順4   ソース・データベースDBA: パブリッシャに対する権限の作成および付与

ソース・データベースDBAは、ソース・データベースのパブリッシャとなるユーザー(source_cdcpubなど)を作成し、必要な権限をそのパブリッシャに付与することで、ソース・データベースからステージング・データベースのパブリッシャに接続するためのデータベース・リンクをそのパブリッシャが設定できるようにします(「パブリッシャとなるユーザーの作成」を参照)。次に例を示します。

CREATE USER source_cdcpub IDENTIFIED EXTERNALLY
QUOTA UNLIMITED ON SYSTEM
QUOTA UNLIMITED ON SYSAUX;
GRANT CREATE SESSION TO source_cdcpub;
GRANT DBA TO source_cdcpub;
GRANT CREATE DATABASE LINK TO source_cdcpub;
GRANT EXECUTE on DBMS_CDC_PUBLISH TO source_cdcpub;
GRANT EXECUTE_CATALOG_ROLE TO source_cdcpub;
GRANT SELECT_CATALOG_ROLE TO source_cdcpub;
EXECUTE DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGE(
GRANTEE=> 'source_cdcpub');

手順5   ステージング・データベースDBA: パブリッシャに対する権限の作成および付与

ステージング・データベースDBAは、チェンジ・データ・キャプチャのパブリッシャとなるユーザー(staging_cdcpubなど)を作成し、必要な権限をそのパブリッシャに付与することで、基本的なOracle Streams操作の実行、ステージング・データベースからソース・データベースのパブリッシャに接続するデータベース・リンクの作成、ソース・データベース上でのチェンジ・ソースの作成、およびステージング・データベース上でのチェンジ・セットおよびチェンジ・テーブルの作成を、そのパブリッシャが実行できるようにします(「パブリッシャとなるユーザーの作成」を参照)。次に例を示します。

CREATE USER staging_cdcpub IDENTIFIED EXTERNALLY
DEFAULT TABLESPACE ts_cdcpub
QUOTA UNLIMITED ON SYSTEM;
GRANT CREATE SESSION TO staging_cdcpub;
GRANT CREATE TABLE TO staging_cdcpub;
GRANT CREATE TABLESPACE TO staging_cdcpub;
GRANT UNLIMITED TABLESPACE TO staging_cdcpub;
GRANT SELECT_CATALOG_ROLE TO staging_cdcpub;
GRANT EXECUTE_CATALOG_ROLE TO staging_cdcpub;
GRANT CONNECT, RESOURCE, DBA TO staging_cdcpub;
GRANT CREATE SEQUENCE TO staging_cdcpub;
EXECUTE DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGE(grantee => 'staging_cdcpub');
GRANT CREATE DATABASE LINK TO staging_cdcpub;

手順6   ソース・データベースのパブリッシャ: データベース・リンクの作成

ソース・データベースのパブリッシャは、ソース・データベースからステージング・データベースへのリンクを作成します。GLOBAL_NAMES初期化パラメータをTRUEに設定しているので、データベース・リンクの名前はステージング・データベースの名前と同じになります。このリンクは、ステージング・データベースのパブリッシャのユーザー名およびパスワードを使用してステージング・データベースに接続します。この例では、ステージング・データベースの名前はstaging_dbになっています。

CREATE DATABASE LINK staging_db
CONNECT TO staging_cdcpub IDENTIFIED BY Stg395V3
USING 'staging_db';

データベース・リンクの詳細は、『Oracle Database管理者ガイド』を参照してください。

手順7   ステージング・データベースのパブリッシャ: データベース・リンクの作成

ステージング・データベースのパブリッシャは、ステージング・データベースからソース・データベースへのリンクを作成します。ここでも、GLOBAL_NAMES初期化パラメータをTRUEに設定しているので、データベース・リンクの名前はソース・データベースの名前と同じになります。このリンクは、ソース・データベースのパブリッシャのユーザー名およびパスワードを使用してソース・データベースに接続します。この例では、ソース・データベースの名前はsource_dbになっています。

CREATE DATABASE LINK source_db
CONNECT TO source_cdcpub IDENTIFIED BY Lvh412A7
USING 'source_db';

このデータベース・リンクは、分散HotLogのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルの作成、変更または削除時には存在している必要があります。ただし、このデータベース・リンクは、変更のキャプチャのために必要ではありません。必要な分散HotLogのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルを配置および有効化すると、変更のキャプチャを妨げずにこのデータベース・リンクを削除できます。データベース・リンクを削除した場合、分散HotLogのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルを作成、変更または削除する際に再作成する必要があります。

手順8   ステージング・データベースのパブリッシャ: チェンジ・ソース・データベースの識別およびチェンジ・ソースの作成

ステージング・データベースのパブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_HOTLOG_CHANGE_SOURCEプロシージャを使用して、ソース・データベース上に分散HotLogチェンジ・ソースを作成します。

チェンジ・データ・キャプチャによってチェンジ・ソースが作成される際に、関連するOracle Streamsの取得プロセスも作成される(開始はされない)ことに注意してください。

パブリッシャは、分散HotLogチェンジ・ソースを作成し、ステージング・データベースからソース・データベースに定められるデータベース・リンクを指定します。データベース・リンクの名前は、ソース・データベースの名前と同じです。

BEGIN
DBMS_CDC_PUBLISH.CREATE_HOTLOG_CHANGE_SOURCE(
change_source_name => 'CHICAGO',
description => 'test source',
source_database => 'source_db');
END;
/

手順9   ステージング・データベースのパブリッシャ: チェンジ・セットの作成

パブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_CHANGE_SETプロシージャを使用してチェンジ・セットを作成します。分散HotLogチェンジ・ソースには、同じステージング・データベース上の1つ以上のチェンジ・セットを格納できます。開始および終了の日付をパブリッシャが指定することはできません。

チェンジ・データ・キャプチャによってチェンジ・セットが作成される際に、関連するOracle Streamsの適用プロセスも作成される(開始はされない)ことに注意してください。

次の例では、今日から始まる変更をキャプチャし、無期限に変更データをキャプチャし続けるCHICAGO_DAILYというチェンジ・セットの作成方法を示します。将来のある時点で、パブリッシャがこのチェンジ・セットでの変更データのキャプチャを停止する場合は、そのチェンジ・セットを無効にしてから削除する必要があります。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_SET(
change_set_name => 'CHICAGO_DAILY',
description => 'change set for product info',
change_source_name => 'CHICAGO',
stop_on_ddl => 'y');
END;
/

手順10   ステージング・データベースのパブリッシャ: チェンジ・テーブルの作成

パブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャを使用してチェンジ・テーブルを作成します。

パブリッシャは、公開される各ソース・テーブルについて、1つ以上のチェンジ・テーブルを作成し、含める列、およびキャプチャする変更データのビフォア・イメージおよびアフター・イメージの組合せを指定します。

パブリッシャは、DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャのoptions_stringフィールドを設定して、チェンジ・テーブルの物理プロパティと表領域プロパティを厳密に制御できます。options_stringフィールドには、CREATE TABLE文で使用可能な、パーティション化以外の任意のオプションを指定できます。この例では、チェンジ・セットの表領域を指定しています。この例では、パブリッシャがすでにTS_CHICAGO_DAILY表領域を作成済であるものとします。

次の例では、ソース・データベースのソース・テーブルに加えられた変更をキャプチャするチェンジ・テーブルをステージング・データベースに作成します。例では、サンプル表sh.productsを使用します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLE(
owner             => 'staging_cdcpub',
change_table_name => 'products_ct',
change_set_name   => 'CHICAGO_DAILY',
source_schema     => 'SH',
source_table      => 'PRODUCTS',
column_type_list  => 'PROD_ID NUMBER(6), PROD_NAME VARCHAR2(50),
PROD_LIST_PRICE NUMBER(8,2),
JOB_ID VARCHAR2(10), DEPARTMENT_ID NUMBER(4)',
capture_values    => 'both',
rs_id             => 'y',
row_id            => 'n',
user_id           => 'n',
timestamp         => 'n',
object_id         => 'n',
source_colmap     => 'n',
target_colmap     => 'y',
options_string    => 'TABLESPACE TS_CHICAGO_DAILY');
END;
/

この例では、チェンジ・セットCHICAGO_DAILY内にproducts_ctという名前のチェンジ・テーブルを作成します。column_type_listパラメータには、このチェンジ・テーブルによりキャプチャされる列を指定します。source_schemaおよびsource_tableパラメータには、ソース・データベース(ステージング・データベースではない)にあるスキーマおよびソース・テーブルを指定します。

例におけるcapture_values設定は、更新操作で変更された各行について、変更データが2つの別個の行を持つことを示します。1つ目の行には更新前の行値が含まれ、2つ目の行には更新後の行値が含まれます。分散HotLogチェンジ・セットの場合、object_idおよびsource_colmapキャプチャ値は、'n'に設定する必要があります。チェンジ・ソースのリリースが9.2または10.1の場合は、row_idおよびuser_idも'n'に設定する必要があります。

詳細は、「チェンジ・テーブルの管理」を参照してください。

手順11   ステージング・データベースのパブリッシャ: チェンジ・ソースの有効化

分散HotLogチェンジ・ソースは作成時には常に無効化されているため、パブリッシャがチェンジ・ソースを変更して有効化する必要があります。チェンジ・ソースを有効化すると、Oracle Streamsの取得プロセスが開始されます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_HOTLOG_CHANGE_SOURCE(
change_source_name => 'CHICAGO',
enable_source      => 'Y');
END;
/

手順12   ステージング・データベースのパブリッシャ: チェンジ・セットの有効化

非同期チェンジ・セットは作成時には常に無効化されているため、パブリッシャがチェンジ・セットを変更して有効化する必要があります。チェンジ・セットを有効化すると、Oracle Streamsの適用プロセスが開始されます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET(
change_set_name => 'CHICAGO_DAILY',
enable_capture  => 'y');
END;
/

手順13   ステージング・データベースのパブリッシャ: サブスクライバへのアクセス権の付与

パブリッシャは、ステージング・データベースにおけるユーザーおよびロールに対してチェンジ・テーブルへのSELECT権限の付与および取消しを行うことで、サブスクライバの変更データへのアクセスを制御します。パブリッシャは、特定のチェンジ・テーブルへのアクセス権を付与します。この手順を行わないと、サブスクライバはどのような変更データにもアクセスできません。この例では、ユーザーsubscriber1がすでに存在しているものとします。

GRANT SELECT ON staging_cdcpub.products_ct TO subscriber1;

これで、チェンジ・データ・キャプチャの分散HotLogシステムが設定され、subscriber1がサブスクリプションを作成する準備ができました。

非同期AutoLogの公開の実行

チェンジ・データ・キャプチャはOracle Streamsのダウンストリーム・キャプチャを使用して、非同期AutoLogの公開を実行します。チェンジ・データ・キャプチャのステージング・データベースは、Streams環境において、ダウンストリーム・データベースとみなされます。非同期AutoLogオンライン・オプションでは、Streamsのリアルタイム・ダウンストリーム・キャプチャが使用されます。非同期AutoLogアーカイブ・オプションでは、Streamsのアーカイブ・ログ・ダウンストリーム・キャプチャが使用されます。Oracle Streamsのダウンストリーム・キャプチャの詳細は、『Oracle Streams概要および管理』を参照してください。

非同期AutoLogのチェンジ・データ・キャプチャでは、パブリッシャは、新しいチェンジ・ソースに加え、個々のソース・テーブルに加えられる変更を格納するチェンジ・テーブルおよびチェンジ・セットを作成します。

非同期AutoLogの公開のために、REDO転送サービス、Streamsおよびチェンジ・データ・キャプチャを設定する手順は、ソース・データベースとステージング・データベースの両方で実行する必要があります。ソース・データベースとステージング・データベースは通常、別個のシステム上にあるため、この例では、ソース・データベースDBA、ステージング・データベースDBAおよびパブリッシャが別々のユーザーであるものとします。

手順1   ソース・データベースDBA: ソース・データベースからのREDOログ・ファイルのコピーの準備

ソース・データベースDBAおよびステージング・データベースDBAは、次のように、REDO転送サービスを設定して、ソース・データベースからステージング・データベースにREDOログ・ファイルをコピーし、ステージング・データベースでこれらのREDOログ・ファイルを受け取る準備をする必要があります。

1. ソース・データベースDBAはOracle Netを構成して、ソース・データベースがステージング・データベースと通信できるようにします。Oracle Netについては、『Oracle Database Net Services管理者ガイド』を参照してください。

2. ソース・データベースDBAは、「チェンジ・データ・キャプチャの公開用初期化パラメータの設定」で説明しているように、ソース・データベース上でデータベース初期化パラメータを設定します。次のコード例のSTAGINGDBは、ステージング・データベースのネットワーク名です。
次に、AutoLogオンライン・オプションの例を示します。

compatible = 11.0
log_archive_dest_1 ="location=/oracle/dbs mandatory reopen=5"
log_archive_dest_2 ="service=stagingdb lgwr async optional noregister reopen=5
valid_for=(online_logfile,primary_role)"
log_archive_dest_state_1 = enable
log_archive_dest_state_2 = enable
log_archive_format="arch_%s_%t_%r.dbf"

次に、AutoLogアーカイブ・オプションの例を示します。

compatible = 11.0
log_archive_dest_1="location=/oracle/dbs mandatory reopen=5"
log_archive_dest_2 = "service=stagingdb arch optional noregister reopen=5
template=/usr/oracle/dbs/arch_%s_%t_%r.dbf"
log_archive_dest_state_1 = enable
log_archive_dest_state_2 = enable
log_archive_format="arch_%s_%t_%r.dbf"

REDO転送サービスの詳細は、『Oracle Data Guard概要および管理』を参照してください。

手順2   ステージング・データベースDBA: データベース初期化パラメータの設定

ステージング・データベースは非同期Autolog CDCのARCHIVELOGモードで実行する必要があります。ステージング・データベースDBAは、「チェンジ・データ・キャプチャの公開用初期化パラメータの設定」で説明しているように、ステージング・データベース上でデータベース初期化パラメータを設定します。これらの例では、1つのチェンジ・セットが定義されます。STREAMS_POOL_SIZEの現行値は50MB以上です。

次に、AutoLogオンライン・オプションの例を示します。

compatible = 11.0
global_names = true
java_pool_size = 50000000
log_archive_dest_1="location=/oracle/dbs mandatory reopen=5
valid_for=(online_logfile,primary_role)"
log_archive_dest_2="location=/usr/oracle/dbs mandatory
valid_for=(standby_logfile,primary_role)"
log_archive_dest_state_1=enable
log_archive_dest_state_2=enable
log_archive_format="arch_%s_%t_%r.dbf"
job_queue_processes = 2
parallel_max_servers = <current_value> + 5
processes = <current_value> + 7
sessions = <current value> + 2
streams_pool_size = <current_value> + 21 MB
undo_retention = 3600

次に、AutoLogアーカイブ・オプションの例を示します。

compatible = 11.0
global_names = true
java_pool_size = 50000000
log_archive_dest_1="location=/oracle/dbs mandatory reopen=5
valid_for=(online_logfile,primary_role)"
log_archive_dest_2="location=/oracle/stdby mandatory
valid_for=(standby_logfile,primary_role)"
log_archive_dest_state_1=enable
log_archive_dest_state_2=enable
log_archive_format="arch_%s_%t_%r.dbf"
job_queue_processes = 2
parallel_max_servers = <current_value> + 5
processes = <current_value> + 7
sessions = <current value> + 2
streams_pool_size = <current_value> + 21 MB
undo_retention = 3600

手順3   ソース・データベースDBA: ソース・データベースの変更

ソース・データベースDBAは、次の3つのタスクを実行します。2つ目は必須です。1つ目と3つ目はオプションですが、実行することをお薦めします。データベースは現在、ARCHIVELOGモードで実行されているものとします。

1. データベースをFORCE LOGGINGロギング・モードに設定し、非同期チェンジ・データ・キャプチャではキャプチャできない、ソース・データベースでのロギングされないダイレクト書込みを回避します。

ALTER DATABASE FORCE LOGGING;

2. サプリメンタル・ロギングを有効化します。サプリメンタル・ロギングによって、更新操作を実行するとすぐに、追加の列データがREDOログ・ファイルに配置されます。

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

3. ソース・テーブルでキャプチャされるすべての列に対して、無条件のログ・グループを作成します。変更されない、無条件のログ・グループにないソース・テーブルの列は、実際のソース・テーブルの値を反映せずに、チェンジ・テーブルにおいてNULLとなります。この例では、sh.products表の行のみをキャプチャします。ソース・データベースDBAは、チェンジ・テーブルが作成される各ソース・テーブルについて、この手順を繰り返します。

ALTER TABLE sh.products
ADD SUPPLEMENTAL LOG GROUP log_group_products
(PROD_ID, PROD_NAME, PROD_LIST_PRICE) ALWAYS;

行の列が更新されたらすぐにその行のすべての列値をキャプチャする場合、ALTER TABLE文に各列を1つずつリストするのではなく、次の文を使用できます。ただし、すべての列を必要としない場合、この形式のALTER TABLE文は使用しないでください。すべての列のロギングを実行すると、選択した列のみのロギングよりも大きなオーバーヘッドが生じます。

ALTER TABLE sh.products ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

ARCHIVELOGモードでのデータベースの運用については、『Oracle Database管理者ガイド』を参照してください。サプリメンタル・ロギングの詳細は、「非同期チェンジ・データ・キャプチャとサプリメンタル・ロギング」および『Oracle Databaseユーティリティ』を参照してください。

手順4   ステージング・データベースDBA: スタンバイREDOログ・ファイルの作成

この手順は、AutoLogオンライン・オプションにのみ必要となります。

ステージング・データベースDBAは、ステージング・データベース上にスタンバイREDOログ・ファイルを実際に作成する必要があります。

1. ソース・データベース上で使用されるログ・ファイルのサイズを決定します。スタンバイREDOログ・ファイルはこのサイズと同じかそれ以上にする必要があるためです。ソース・データベースに対してV$LOGを問い合せると、ソース・データベースのログ・ファイルのサイズを確認できます。

2. ステージング・データベース上で必要なスタンバイ・ログ・ファイル・グループの数を決定します。この数は、ソース・データベース上のオンライン・ログ・ファイル・グループの数より少なくとも1以上大きな数にする必要があります。ソース・データベースに対してV$LOGを問い合せると、ソース・データベース上のオンライン・ログ・ファイル・グループの数を確認できます。

3. SQL文ALTER DATABASE ADD STANDBY LOGFILEを使用して、ステージング・データベースにスタンバイ・ログ・ファイル・グループを追加します。

ALTER DATABASE ADD STANDBY LOGFILE GROUP 3
('/oracle/dbs/slog3a.rdo', '/oracle/dbs/slog3b.rdo') SIZE 500M;

ステージング・データベースに対してV$STANDBY_LOGを問い合せると、スタンバイ・ログ・ファイル・グループが正常に追加されていることが確認できます。

手順5   ステージング・データベースDBA: パブリッシャに対する権限の作成および付与

ステージング・データベースDBAは、チェンジ・データ・キャプチャのパブリッシャとなるユーザー(cdcpubなど)を作成し、必要な権限をそのパブリッシャに付与することで、チェンジ・データ・キャプチャのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルをステージング・データベース上に作成するために必要な基本的なOracle Streams操作をそのパブリッシャが実行できるようにします(「パブリッシャとなるユーザーの作成」を参照)。次に例を示します。

CREATE USER cdcpub IDENTIFIED EXTERNALLY DEFAULT TABLESPACE ts_cdcpub
QUOTA UNLIMITED ON SYSTEM
QUOTA UNLIMITED ON SYSAUX;
GRANT CREATE SESSION TO cdcpub;
GRANT CREATE TABLE TO cdcpub;
GRANT CREATE TABLESPACE TO cdcpub;
GRANT UNLIMITED TABLESPACE TO cdcpub;
GRANT SELECT_CATALOG_ROLE TO cdcpub;
GRANT EXECUTE_CATALOG_ROLE TO cdcpub;
GRANT DBA TO cdcpub;
GRANT CREATE SEQUENCE TO cdcpub;
GRANT EXECUTE on DBMS_CDC_PUBLISH TO cdcpub;
EXECUTE DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGE(grantee => 'cdcpub');

手順6   ソース・データベースDBA: LogMinerデータ・ディクショナリの作成

ソース・データベースDBAは、ソース・データベースでLogMinerデータ・ディクショナリを作成することで、REDO転送サービスによって、このデータ・ディクショナリをステージング・データベースに転送できるようにします。LogMinerデータ・ディクショナリを作成することによって、変更データのキャプチャを開始する直前の表定義が提供されます。チェンジ・データ・キャプチャは、変更データのキャプチャの過程で行われるソース・テーブルのデータ定義言語(DDL)操作で、自動的にデータ・ディクショナリを更新します。これによって、ディクショナリが常にソース・データベース表と同期していることが保証されます。

LogMinerデータ・ディクショナリを作成する際、ソース・データベースDBAは、データ・ディクショナリ作成のSCN値を取得する必要があります。手順8で、パブリッシャはチェンジ・ソースを作成する際に、first_scnパラメータとしてこの値を指定する必要があります。

SET SERVEROUTPUT ON
VARIABLE f_scn NUMBER;
BEGIN
:f_scn := 0;
DBMS_CAPTURE_ADM.BUILD(:f_scn);
DBMS_OUTPUT.PUT_LINE('The first_scn value is ' || :f_scn);
END;
/
The first_scn value is 207722

非同期AutoLogの公開を機能させるには、ソース・データベースDBAは、ソース・テーブルを準備する前にデータ・ディクショナリを作成する(およびその作成を完了させる)ことが重要となります。ソース・データベースDBAは、手順6および手順7をここで示している順番どおりに注意して実行する必要があります。

LogMinerデータ・ディクショナリの詳細は、『Oracle Streams概要および管理』を参照してください。

手順7   ソース・データベースDBA: ソース・テーブルの準備

ソース・データベースDBAは、非同期チェンジ・データ・キャプチャに対して、各ソース・テーブルをインスタンス化することによって、ソース・データベース上にソース・テーブルを準備する必要があります。これによって、基底のOracle Streams環境で、各ソース・テーブルの変更のキャプチャに必要な情報が記録されます。ソース・テーブルの構造および列データ型は、チェンジ・データ・キャプチャでサポートされる必要があります。詳細は、「非同期チェンジ・データ・キャプチャでサポートされるデータ型と表構造」を参照してください。

BEGIN
DBMS_CAPTURE_ADM.PREPARE_TABLE_INSTANTIATION(
TABLE_NAME => 'sh.products');
END;
/

手順8   ソース・データベースDBA: ソース・データベースのグローバル名の取得

手順8で、パブリッシャは、ソース・データベースのグローバル名を参照する必要があります。ソース・データベースのグローバル名は、AutoLogチェンジ・ソースを作成するためにステージング・データベース上で使用されます。ソース・データベースDBAは、ソース・データベースにおけるGLOBAL_NAMEビューのGLOBAL_NAME列を問い合せて、パブリッシャのこの情報を取得できます。

SELECT GLOBAL_NAME FROM GLOBAL_NAME;
GLOBAL_NAME
----------------------------------------------------------------------------
HQDB

手順9   ステージング・データベースのパブリッシャ: 各チェンジ・ソース・データベースの識別およびチェンジ・ソースの作成

パブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_AUTOLOG_CHANGE_SOURCEプロシージャを使用してチェンジ・ソースを作成します。

キャプチャ・システムの管理プロセスは、チェンジ・ソースの作成から始まります。チェンジ・ソースは、データのキャプチャ対象のソース・データベースを記述し、ソース・データベースとステージング・データベースとの間の関係を管理します。チェンジ・ソースは、ソース・データベースからのデータ・ディクショナリ作成のSCNをfirst_scnパラメータとして常に指定します。

パブリッシャは、データ・ディクショナリ作成のSCN、およびグローバル・データベース名をソース・データベースDBAから取得します(それぞれ手順6と手順8を参照)。パブリッシャが、ソース・データベースDBAからfirst_scnパラメータ値に使用する値を取得できない場合は、適切な権限を使用し、ソース・データベースにおけるV$ARCHIVED_LOGビューの問合せを実行して、値を確認できます。詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』のDBMS_CDC_PUBLISHの章を参照してください。

ステージング・データベースでは、パブリッシャはAutoLogチェンジ・ソースを作成し、source_databaseパラメータ値としてグローバル名を、first_scnパラメータ値としてデータ・ディクショナリ作成のSCNを指定します。次のようにしてAutoLogオンライン・チェンジ・ソースを作成します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_AUTOLOG_CHANGE_SOURCE(
change_source_name  => 'CHICAGO',
description         => 'test source',
source_database     => 'HQDB',
first_scn           => 207722,
online_log          => 'y');
END;
/

次のようにしてAutoLogアーカイブ・チェンジ・ソースを作成します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_AUTOLOG_CHANGE_SOURCE(
change_source_name   => 'CHICAGO',
description          => 'test source',
source_database      => 'HQDB',
first_scn            => 207722);
END;
/

手順10   ステージング・データベースのパブリッシャ: チェンジ・セットの作成

パブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_CHANGE_SETプロシージャを使用してチェンジ・セットを作成します。パブリッシャは、データ・キャプチャの開始および終了箇所を示すために、開始および終了の日付をオプションで提供できます。

チェンジ・データ・キャプチャによってチェンジ・セットが作成される際に、関連するOracle Streamsの取得プロセスと適用プロセスも作成される(開始はされない)ことに注意してください。

次の例では、今日から始まる変更をキャプチャし、無期限に変更データをキャプチャし続けるCHICAGO_DAILYというチェンジ・セットの作成方法を示します。将来のある時点で、パブリッシャがこのチェンジ・セットでの変更データのキャプチャを停止する場合は、そのチェンジ・セットを無効にしてから削除する必要があります。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_SET(
change_set_name    => 'CHICAGO_DAILY',
description        => 'change set for product info',
change_source_name => 'CHICAGO',
stop_on_ddl        => 'y');
END;
/

手順11   ステージング・データベースのパブリッシャ: チェンジ・テーブルの作成

パブリッシャは、ステージング・データベース上でDBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャを使用してチェンジ・テーブルを作成します。

パブリッシャは、公開される各ソース・テーブルについて、1つ以上のチェンジ・テーブルを作成し、含める列、およびキャプチャする変更データのビフォア・イメージおよびアフター・イメージの組合せを指定します。

パブリッシャは、DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャのoptions_stringフィールドを設定して、チェンジ・テーブルの物理プロパティと表領域プロパティを厳密に制御できます。options_stringフィールドには、CREATE TABLE文で使用可能な、パーティション化以外の任意のオプションを指定できます。この例では、チェンジ・セットの表領域を指定しています。この例では、パブリッシャがすでにTS_CHICAGO_DAILY表領域を作成済であるものとします。

次の例では、ソース・データベースのソース・テーブルに加えられた変更をキャプチャするチェンジ・テーブルをステージング・データベースに作成します。例では、サンプル表sh.productsを使用します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLE(
owner             => 'cdcpub',
change_table_name => 'products_ct',
change_set_name   => 'CHICAGO_DAILY',
source_schema     => 'SH',
source_table      => 'PRODUCTS',
column_type_list  => 'PROD_ID NUMBER(6), PROD_NAME VARCHAR2(50),
PROD_LIST_PRICE NUMBER(8,2)',
JOB_ID VARCHAR2(10), DEPARTMENT_ID NUMBER(4)',
capture_values    => 'both',
rs_id             => 'y',
row_id            => 'n',
user_id           => 'n',
timestamp         => 'n',
object_id         => 'n',
source_colmap     => 'n',
target_colmap     => 'y',
options_string    => 'TABLESPACE TS_CHICAGO_DAILY');
END;
/

この例では、チェンジ・セットCHICAGO_DAILY内にproducts_ctという名前のチェンジ・テーブルを作成します。column_type_listパラメータには、このチェンジ・テーブルによりキャプチャされる列を指定します。source_schemaおよびsource_tableパラメータには、ソース・データベース(ステージング・データベースではない)にあるスキーマおよびソース・テーブルを指定します。

例におけるcapture_values設定は、更新操作で変更された各行について、変更データが2つの別個の行を持つことを示します。1つ目の行には更新前の行値が含まれ、2つ目の行には更新後の行値が含まれます。

詳細は、「チェンジ・テーブルの管理」を参照してください。

手順12   ステージング・データベースのパブリッシャ: チェンジ・セットの有効化

非同期チェンジ・セットは作成時には常に無効化されているため、パブリッシャがチェンジ・セットを変更して有効化する必要があります。チェンジ・セットを有効化すると、Oracle Streamsの取得プロセスおよび適用プロセスが開始されます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET(
change_set_name => 'CHICAGO_DAILY',
enable_capture  => 'y');
END;
/

手順13   ソース・データベースDBA: ソース・データベースでのREDOログ・ファイルの切替え

データのキャプチャを開始するには、ログ・ファイルがアーカイブされる必要があります。ソース・データベースDBAは、現行のREDOログ・ファイルを切り替えることによって、プロセスを開始できます。

ALTER SYSTEM SWITCH LOGFILE;

手順14   ステージング・データベースのパブリッシャ: サブスクライバへのアクセス権の付与

パブリッシャは、ステージング・データベースにおけるユーザーおよびロールに対してチェンジ・テーブルへのSQLのSELECT権限の付与および取消しを行うことで、サブスクライバの変更データへのアクセスを制御します。パブリッシャは、特定のチェンジ・テーブルへのアクセス権を付与します。この手順を行わないと、サブスクライバはどのような変更データにもアクセスできません。この例では、ユーザーsubscriber1がすでに存在しているものとします。

GRANT SELECT ON cdcpub.products_ct TO subscriber1;

これで、チェンジ・データ・キャプチャの非同期AutoLogシステムが設定され、subscriber1がサブスクリプションを作成する準備ができました。

変更データのサブスクライブ

パブリッシャがチェンジ・テーブルを作成する際に、チェンジ・データ・キャプチャによってパブリケーションIDが割り当てられ、すべてのパブリケーションIDのリストがDBA_PUBLISHED_COLUMNSビューに格納されます。パブリケーションIDとは、パブリッシャが定義した各チェンジ・テーブルに、チェンジ・データ・キャプチャによって割り当てられる数値のことです。

サブスクライバは、使用する1つ以上のソース・テーブルを登録し、これらの表へのサブスクリプションを取得します。サブスクライバに十分なアクセス権があるとすると、サブスクライバは次のいずれかの方法で、パブリッシャが1つ以上のチェンジ・テーブルを作成したどのソース・テーブルに対してもサブスクライブできます。

- 対象の列およびソース・テーブルを指定する
対象の列を含むパブリケーションが複数ある場合、チェンジ・データ・キャプチャはユーザーのかわりに1つを選択します。

- 対象の列およびパブリケーションIDを指定する
単一のソース・テーブルに複数のパブリケーションがあり、これらのパブリケーションでいくつかの列が共有されている際に、共有されている列のどれかが単一のサブスクリプションで使用される場合、サブスクライバは、ソース・テーブルではなくパブリケーションIDを指定する必要があります。

次の手順では、両方のシナリオを例示する例を示します。

手順1   サブスクライバがアクセス権を付与されているソース・テーブルの検索

サブスクライバは、DBA_SOURCE_TABLESビューを問い合せて、サブスクライバがアクセス権を付与されている公開済のソース・テーブルをすべて参照します。

SELECT * FROM DBA_SOURCE_TABLES;

SOURCE_SCHEMA_NAME             SOURCE_TABLE_NAME
------------------------------ ------------------------------
SH                             PRODUCTS

手順2   サブスクライバがアクセス権を付与されている列およびチェンジ・セット名の検索

サブスクライバは、DBA_PUBLISHED_COLUMNSビューを問い合せて、サブスクライバがアクセス権を付与されているsh.products表のチェンジ・セット、列およびパブリケーションIDをすべて参照します。

SELECT UNIQUE CHANGE_SET_NAME, COLUMN_NAME, PUB_ID
FROM DBA_PUBLISHED_COLUMNS
WHERE SOURCE_SCHEMA_NAME ='SH' AND SOURCE_TABLE_NAME = 'PRODUCTS';

CHANGE_SET_NAME     COLUMN_NAME        PUB_ID
----------------    ------------------ ------------
CHICAGO_DAILY       PROD_ID            41494
CHICAGO_DAILY       PROD_LIST_PRICE    41494
CHICAGO_DAILY       PROD_NAME          41494

手順3   サブスクリプションの作成

サブスクライバは、DBMS_CDC_SUBSCRIBE.CREATE_SUBSCRIPTIONプロシージャをコールして、サブスクリプションを作成します。

次の例は、サブスクライバが対象のチェンジ・セット(CHICAGO_DAILY)を指定してから、サブスクリプションが存在する期間を通して使用される一意のサブスクリプション名を指定する方法を示しています。

BEGIN
DBMS_CDC_SUBSCRIBE.CREATE_SUBSCRIPTION(
change_set_name   => 'CHICAGO_DAILY',
description       => 'Change data for PRODUCTS',
subscription_name => 'SALES_SUB');
END;
/

手順4   ソース・テーブルとその列へのサブスクライブ

サブスクライバは、DBMS_CDC_SUBSCRIBE.SUBSCRIBEプロシージャをコールして、サブスクライバが必要とするソース・テーブルの列を指定します。

サブスクリプションには、同じチェンジ・セットによって参照される1つ以上のソース・テーブルを含めることができます。

次の例では、サブスクライバは、PRODUCTS表のPROD_ID列、PROD_NAME列およびPROD_LIST_PRICE列を参照します。手順2の問合せに示すように、これらの列はすべて同じパブリケーションに含まれ、サブスクライバはそのパブリケーションに対するアクセス権を所有しているので、次のコールを使用できます。

BEGIN
DBMS_CDC_SUBSCRIBE.SUBSCRIBE(
subscription_name => 'SALES_SUB',
source_schema     => 'SH',
source_table      => 'PRODUCTS',
column_list       => 'PROD_ID, PROD_NAME, PROD_LIST_PRICE',
subscriber_view   => 'SALES_VIEW');
END;
/

ただし、セキュリティの理由から、パブリッシャがこれらすべての列を含む単一のチェンジ・テーブルを作成していないと仮定します。手順2に示す結果のかわりに、DBA_PUBLISHED_COLUMNSビューの問合せでは、次の例に示すように、対象の列が複数のパブリケーションに含まれていることが示されるものとします。

SELECT UNIQUE CHANGE_SET_NAME, COLUMN_NAME, PUB_ID
FROM DBA_PUBLISHED_COLUMNS
WHERE SOURCE_SCHEMA_NAME ='SH' AND SOURCE_TABLE_NAME = 'PRODUCTS';

CHANGE_SET_NAME     COLUMN_NAME        PUB_ID
----------------    ------------------ ------------
CHICAGO_DAILY       PROD_ID            34883
CHICAGO_DAILY       PROD_NAME          34885
CHICAGO_DAILY       PROD_LIST_PRICE    34883
CHICAGO_DAILY       PROD_ID            34885

戻されたこのデータから、PROD_ID列がパブリケーション34883とパブリケーション34885の両方に含まれていることがわかります。単一のサブスクライブ・コールでは、単一のパブリケーションの使用可能な列を指定する必要があります。したがって、サブスクライバが両方のパブリケーションの列にサブスクライブする場合は、PROD_IDを使用してサブスクライバ・ビュー間を結合してから、それぞれ異なるパブリケーションIDを指定する次の2つのコールを使用する必要があります。

BEGIN
DBMS_CDC_SUBSCRIBE.SUBSCRIBE(
subscription_name => 'MULTI_PUB',
publication_id    => 34885,
column_list       => 'PROD_ID, PROD_NAME',
subscriber_view   => 'prod_idname');

DBMS_CDC_SUBSCRIBE.SUBSCRIBE(
subscription_name => 'MULTI_PUB',
publication_id    => 34883,
column_list       => 'PROD_ID, PROD_LIST_PRICE',
subscriber_view   => 'prod_price');
END;
/

各DBMS_CDC_SUBSCRIBE.SUBSCRIBEコールでは、一意のサブスクライバ・ビューを指定することに注意してください。

手順5   サブスクリプションのアクティブ化

サブスクライバは、DBMS_CDC_SUBSCRIBE.ACTIVATE_SUBSCRIPTIONプロシージャをコールして、サブスクリプションをアクティブ化します。

サブスクライバは、ソース・テーブル(またはパブリケーション)へのサブスクライブを完了し、変更データを受け取る準備ができた時点で、このプロシージャをコールします。サブスクライブするソース・テーブルが1つの場合も複数の場合も、サブスクライバはACTIVATE_SUBSCRIPTIONプロシージャを1回コールするだけで済みます。

ACTIVATE_SUBSCRIPTIONプロシージャは空のサブスクライバ・ビューを作成します。この時点で、DBMS_CDC_SUBSCRIBE.SUBSCRIBEコールを行うことができます。

BEGIN
DBMS_CDC_SUBSCRIBE.ACTIVATE_SUBSCRIPTION(
subscription_name => 'SALES_SUB');
END;
/

手順6   変更データの次のセットの取得

サブスクライバは、DBMS_CDC_SUBSCRIBE.EXTEND_WINDOWプロシージャをコールして、次に使用できる変更データのセットを取得します。これによって、サブスクリプション・ウィンドウの高位限界が設定されます。次に例を示します。

BEGIN
DBMS_CDC_SUBSCRIBE.EXTEND_WINDOW(
subscription_name => 'SALES_SUB');
END;
/

これがEXTEND_WINDOWプロシージャに対するサブスクライバの最初のコールである場合、サブスクリプション・ウィンドウには、パブリケーション内のすべての変更データが含まれます。それ以外の場合、サブスクリプション・ウィンドウには、EXTEND_WINDOWプロシージャへの最後のコールから作成された新しい変更データがすべて追加されます。

新しい変更データが追加されていない場合、サブスクリプション・ウィンドウは変化しません。

手順7   サブスクライバ・ビューの内容の読取りと問合せ

サブスクライバは、サブスクライバ・ビューでSQL文のSELECTを使用して、変更データを(サブスクリプション・ウィンドウの現行の境界内で)問い合せます。これは、サブスクリプションに含まれるサブスクライバ・ビューごとに行うことができます。次に例を示します。

SELECT PROD_ID, PROD_NAME, PROD_LIST_PRICE FROM SALES_VIEW;

PROD_ID    PROD_NAME                                          PROD_LIST_PRICE
---------- -------------------------------------------------- ---------------
30         And 2 Crosscourt Tee Kids                          14.99
30         And 2 Crosscourt Tee Kids                          17.66
10         Gurfield& Murks Pleated Trousers                   17.99
10         Gurfield& Murks Pleated Trousers                   21.99

サブスクライバ・ビューの名前SALES_VIEWは、手順4でサブスクライバがDBMS_CDC_SUBSCRIBE.SUBSCRIBEプロシージャをコールしたときに指定したものです。

手順8   変更データの現行セットが必要なくなったことの指定

サブスクライバは、DBMS_CDC_SUBSCRIBE.PURGE_WINDOWプロシージャを使用して、現在の変更データのセットを必要としなくなったことを、チェンジ・データ・キャプチャに知らせます。これは、チェンジ・データ・キャプチャによるチェンジ・テーブル内のデータ量の管理およびサブスクリプション・ウィンドウの低位限界の設定に役立ちます。DBMS_CDC_SUBSCRIBE.PURGE_WINDOWプロシージャをコールすると、サブスクリプション・ウィンドウが空になります。DBMS_CDC_SUBSCRIBE.PURGE_WINDOWの機能の詳細は、「チェンジ・テーブルの不要なデータのパージ」を参照してください。

次に例を示します。

BEGIN
DBMS_CDC_SUBSCRIBE.PURGE_WINDOW(
subscription_name => 'SALES_SUB');
END;
/

注意:
DBMS_CDC_PUBLISH.PURGEを使用する前にDBMS_CDC_SUBSCRIBE.EXTEND_WINDOWをコールしてください。これによって、まだサブスクライバに必要なデータを保護し、パージしてはいけない重要なデータがあることをパブリッシャに認識させることができます。

手順9   手順6から8の繰返し

サブスクライバは、追加の変更データが必要な間は、手順6から8を繰り返します。

手順10   サブスクリプションの終了

サブスクライバは、DBMS_CDC_SUBSCRIBE.DROP_SUBSCRIPTIONプロシージャを使用して、サブスクリプションを終了します。これは、サブスクリプションの基礎をなすパブリケーションが、無期限に変更データを保持することを回避するために必要です。

BEGIN
DBMS_CDC_SUBSCRIBE.DROP_SUBSCRIPTION(
subscription_name => 'SALES_SUB');
END;
/

公開されたデータの管理

この項では、チェンジ・セットおよびチェンジ・テーブルの管理に関わる管理タスクの情報を提供します。ほとんどの場合、これらのタスクはパブリッシャが実行します。ただし、チェンジ・テーブルから不要なデータをパージする場合は、パブリッシャとサブスクライバの両方で行う必要があります(「チェンジ・テーブルの不要なデータのパージ」を参照)。

この項では次のトピックについて説明します。

- 非同期チェンジ・ソースの管理

- 非同期チェンジ・セットの管理

- チェンジ・テーブルの管理

- Oracle Data Pumpを使用したチェンジ・データ・キャプチャ・オブジェクトのエクスポートおよびインポート

- パブリッシャが変更を行う際のサブスクリプションへの影響

非同期チェンジ・ソースの管理

この項では、パブリッシャが非同期チェンジ・ソースの管理を行うことができるタスクに関する情報を取り上げます。次のトピックで構成されています。

- 非同期分散HotLogチェンジ・ソースの有効化および無効化

非同期分散HotLogチェンジ・ソースの有効化および無効化

パブリッシャは、非同期分散HotLogチェンジ・ソースを有効化または無効化できます。チェンジ・ソースが無効化されると、有効化されるまで新しい変更データを処理できません。

非同期分散HotLogチェンジ・ソースは常に無効な状態で作成されます。

パブリッシャは、次のコールでPRODUCTS_SOURCE非同期分散HotLogチェンジ・ソースを有効化できます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_HOTLOG_CHANGE_SOURCE(
change_source_name => 'PRODUCTS_SOURCE',
enable_source      => 'y');
END;
/

チェンジ・ソースを有効化すると、そのチェンジ・ソースについて、Oracle Streamsの取得プロセスが開始されます。パブリッシャは、同じコールを使用(ただし、enable_sourceを'n'に設定)してPRODUCTS_SOURCE非同期分散HotLogチェンジ・ソースを無効化できます。チェンジ・ソースを無効化すると、そのチェンジ・ソースについて、Oracle Streamsの取得プロセスが停止されます。

非同期分散HotLogチェンジ・ソースおよびそのチェンジ・セットは、変更データの処理およびチェンジ・テーブルの移入を行うために別々に有効化する必要があります。詳細は、「非同期チェンジ・セットの有効化および無効化」を参照してください。

無効化されたチェンジ・ソースでは、新しい変更データを処理できませんが、チェンジ・ソースおよびそのチェンジ・セットを有効化し、変更データを処理できるようにするまで、必要なアーカイブREDOログ・ファイルが使用できる状態であれば、変更データを失うことはありません。Oracleでは、可能なかぎりチェンジ・ソースおよびチェンジ・セットを有効化して、アーカイブREDOログ・ファイルを蓄積しないようにすることをお薦めします。詳細は、「非同期チェンジ・データ・キャプチャおよびREDOログ・ファイル」を参照してください。

非同期チェンジ・セットの管理

この項では、パブリッシャが非同期チェンジ・セットの管理を行うことができるタスクについて説明します。次のトピックで構成されています。

- 開始日および終了日を設定した非同期チェンジ・セットの作成

- 非同期チェンジ・セットの有効化および無効化

- 非同期チェンジ・セットのDDLでのキャプチャの停止

- 非同期チェンジ・セットで戻されたエラーからのリカバリ

開始日および終了日を設定した非同期チェンジ・セットの作成

非同期HotLogおよびAutoLogチェンジ・ソースに関連するチェンジ・セットでは、キャプチャする変更データを制限するために、開始日および終了日をオプションで指定できます。分散HotLogチェンジ・ソースに関連するチェンジ・セットでは、開始日および終了日の指定はできません。開始日を設定していないチェンジ・セットでは、最初に使用可能となった変更データからキャプチャを開始します。終了日を設定していないチェンジ・セットでは、変更データのキャプチャが無制限に続きます。

次の例では、現時点から2日後にキャプチャを開始し、無制限にキャプチャし続けるチェンジ・セットPRODUCTS_SETをAutoLogチェンジ・ソースHQ_SOURCEに作成します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_SET(

change_set_name    => 'PRODUCTS_SET',
description        => 'Products Application Change Set',
change_source_name => 'HQ_SOURCE',
stop_on_ddl        => 'Y',
begin_date         => sysdate+2);

END;
/

非同期チェンジ・セットの有効化および無効化

パブリッシャは、非同期チェンジ・セットを有効化または無効化できます。チェンジ・セットが無効化されると、有効化されるまで新しい変更データを処理できません。

同期チェンジ・セットは常に有効な状態で作成されます。非同期チェンジ・セットは常に無効な状態で作成されます。

パブリッシャは、次のコールでPRODUCTS_SET非同期チェンジ・セットを有効化できます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET(

change_set_name => 'PRODUCTS_SET',
enable_capture  => 'y');

END;
/

非同期HotLogおよびAutoLogチェンジ・セットでは、チェンジ・セットを有効化すると、Oracle Streamsの取得プロセスおよび適用プロセスが開始されます。非同期分散HotLogチェンジ・セットでは、チェンジ・セットを有効化すると、Oracle Streamsの適用プロセスが開始されます。

パブリッシャは、同じコールを使用(ただし、enable_captureを'n'に設定)してPRODUCTS_SET非同期チェンジ・セットを無効化できます。非同期HotLogおよびAutoLogチェンジ・セットでは、チェンジ・セットを無効化すると、Oracle Streamsの取得プロセスおよび適用プロセスが停止されます。非同期分散HotLogチェンジ・セットでは、チェンジ・セットを無効化すると、Oracle Streamsの適用プロセスが停止されます。

非同期分散HotLogチェンジ・ソースおよびそのチェンジ・セットは、変更データの処理およびチェンジ・テーブルの移入を行うために別々に有効化する必要があります。詳細は、「非同期分散HotLogチェンジ・ソースの有効化および無効化」を参照してください。

無効化されたチェンジ・セットでは、新しい変更データを処理できませんが、チェンジ・セットを有効化し、変更データを処理できるようにするまで、必要なアーカイブREDOログ・ファイルが使用できる状態であれば、変更データを失うことはありません。Oracleでは、可能なかぎりチェンジ・セットを有効化して、アーカイブREDOログ・ファイルを蓄積しないようにすることをお薦めします。詳細は、「非同期チェンジ・データ・キャプチャおよびREDOログ・ファイル」を参照してください。

チェンジ・データ・キャプチャは、内部キャプチャ・エラーが発生すると、非同期チェンジ・セットを自動的に無効化できます。パブリッシャは、詳細をアラート・ログで確認し、DDLに対して必要な処理を行うか、内部エラーからリカバリしてから、明示的にチェンジ・セットを有効化する必要があります。詳細は、「非同期チェンジ・セットで戻されたエラーからのリカバリ」を参照してください。

非同期チェンジ・セットのDDLでのキャプチャの停止

パブリッシャは、DDLが検出された場合に、チェンジ・データ・キャプチャでチェンジ・セットが自動的に無効化されるよう指定できます。キャプチャされているソース・テーブル列の削除など、一部のDDLコマンドは、キャプチャに悪影響を及ぼす可能性があります。チェンジ・セットがDDLで停止した場合、パブリッシャには、キャプチャを続行する前に、問題を分析および解決する機会があります。チェンジ・セットがDDLで停止しなかった場合、DDLの発生後に内部キャプチャ・エラーが発生した可能性があります。

パブリッシャは、チェンジ・セットの作成時または変更時に、チェンジ・セットがDDLで停止するかどうかを指定できます。パブリッシャは、次のコールでPRODUCTS_SETチェンジ・セットがDDLで停止するように変更できます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET(

change_set_name  => 'PRODUCTS_SET',
stop_on_ddl      => 'y');

END;
/

パブリッシャは、stop_on_ddlを'n'に設定して、PRODUCTS_SETチェンジ・セットがDDLで停止しないように変更できます。

DDL文によってプロセスが停止される場合、DDL文およびチェンジ・セットに関するメッセージがアラート・ログに書き込まれます。たとえば、TRUNCATE TABLE DDL文によって、PRODUCTS_SETチェンジ・セットの処理が停止される場合、アラート・ログには次のような行が含まれます。

Change Data Capture received DDL for change set PRODUCTS_SET
Change Data Capture received DDL and stopping: truncate table products

次のDDL文では列データ自体は影響されないので、stop_on_ddlパラメータを'Y'に設定している際も、チェンジ・データ・キャプチャが変更データのキャプチャを停止することはありません。

- ANALYZE TABLE

- LOCK TABLE

- 表アクセスへのGRANT権限

- 表アクセスへのREVOKE権限

- 表に対するCOMMENT

- 列に対するCOMMENT

これらの文は、チェンジ・データ・キャプチャ処理への影響を考慮することなく、ソース・データベースで発行可能です。たとえば、ANALYZE TABLEコマンドをPRODUCTSソース・テーブルで発行すると、ステージング・データベースのアラート・ログには、stop_on_ddlパラメータを'Y'に設定している場合に次のような行が含まれます。

Change Data Capture received DDL and ignoring: analyze table products compute statistics

非同期チェンジ・セットで戻されたエラーからのリカバリ

非同期チェンジ・データ・キャプチャ時のエラーは、様々な事情に起因していることが考えられます。チェンジ・セットがDDLで停止した場合は、キャプチャを続行する前にそのDDLを取り除く必要があります。チェンジ・セットがDDLで停止していないが、キャプチャに影響するDDL変更が発生した場合は、Oracleエラーを生じる場合があります。ディスク領域不足など、システムの状態もOracleエラーの原因となる場合があります。

これらのすべての場合で、チェンジ・セットは無効化され、エラーがあるものとしてマークされます。サブスクライバのプロシージャでチェンジ・セットにエラーが検出されると、次のメッセージが戻されます。

ORA-31514: change set disabled due to capture error

パブリッシャは、詳細をアラート・ログで確認して、根本にある問題を解決する必要があります。次に、recover_after_errorおよびremove_ddlパラメータを'y'に設定して、ALTER_CHANGE_SETをコールすることによって、エラーからのリカバリを試みることができます。

パブリッシャは、問題を解決するまで、必要なだけこの手順を繰り返すことができます。リカバリが成功すると、エラーがチェンジ・セットから取り除かれるので、パブリッシャは、非同期チェンジ・セットを有効化できます(「非同期チェンジ・セットの有効化および無効化」を参照)。

パブリッシャは、複数の連続したDDLが存在する場合、チェンジ・セットがそれぞれのDDLで別々に停止することを認識している必要があります。たとえば、2つの連続したDDLがあるとします。チェンジ・セットが最初のDDLで停止した場合、パブリッシャはそのDDLを削除し、そのチェンジ・セットに関するキャプチャを再有効化する必要があります。チェンジ・セットは次に、2つ目のDDLで停止します。パブリッシャはそのDDLを削除し、そのチェンジ・セットに関するキャプチャを再有効化する必要があります。

キャプチャ・エラーの解決に詳細な情報が必要な場合、パブリッシャは、DBA_APPLY_ERRORビューを問い合せて、Streamsの適用エラーに関する情報を参照できます(キャプチャ・エラーはStreamsの適用エラーに対応します)。パブリッシャは、キャプチャ・エラーからのリカバリには常にDBMS_CDC_PUBLISH.ALTER_CHANGE_SETプロシージャを使用する必要があります。リカバリには、Streamsとチェンジ・データ・キャプチャの両方の機能が必要で、DBMS_CDC_PUBLISH.ALTER_CHANGE_SETプロシージャのみが両方の機能を実行するためです。エラー・キューおよび適用エラーについては、『Oracle Streams概要および管理』を参照してください。

次の2つのシナリオでは、チェンジ・データ・キャプチャに戻された2つの異なるタイプのエラーを、パブリッシャが調査して、リカバリする方法を例示します。

ディスク領域不足によるエラー

パブリッシャはアラート・ログの内容を参照して、特定のチェンジ・セットについて戻されているエラーおよび処理されていないSCNを判別できます。たとえば、アラート・ログには、次のような行が含まれる場合があります(LCRは論理変更レコードを指します)。

Change Data Capture has encountered error number: 1688 for change set:
CHICAGO_DAILY
Change Data Capture did not process LCR with scn 219337

パブリッシャは、エラー・メッセージ・テキストについて、DBA_APPLY_ERRORビューを問い合せて、アラート・ログで指定されているエラー番号に関連するメッセージを判別できます。この場合、DBA_APPLY_ERRORビューのAPPLY_NAMEは、アラート・ログで指定されたチェンジ・セットのAPPLY_NAMEと同じです。次に例を示します。

SQL> SELECT ERROR_MESSAGE FROM DBA_APPLY_ERROR
WHERE APPLY_NAME =
(SELECT APPLY_NAME FROM ALL_CHANGE_SETS WHERE SET_NAME ='CHICAGO_DAILY');

ERROR_MESSAGE
--------------------------------------------------------------------------------
ORA-01688: unable to extend table LOGADMIN.CT1 partition P1 by 32 in tablespace
TS_CHICAGO_DAILY

エラーの原因となっている問題を修正したら、パブリッシャは、エラーからのリカバリを試行できます。たとえば、パブリッシャは次のコールを使用して、エラー後にCHICAGO_DAILYチェンジ・セットのリカバリを試行できます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET(
change_set_name     => 'CHICAGO_DAILY',
recover_after_error => 'y');
END;
/

リカバリが成功しないとエラーが戻されるので、パブリッシャは、問題の解決を試行するためにさらなる操作を行うことになります。パブリッシャは、問題を解決するまで、必要なだけリカバリ手順を再試行できます。

注意:
リカバリが成功したら、パブリッシャはチェンジ・セットを忘れずに有効化する必要があります。有効化すると、チェンジ・データ・キャプチャによって、エラーが発生した論理変更レコード(LCR)が処理されます。この際、変更データが失われることはありません。

DDLでの停止によるエラー

SQL文のTRUNCATE TABLEをPRODUCTSソース・テーブルに対して発行して、stop_on_ddlパラメータを'Y'に設定した状態で、チェンジ・セットの有効化を試みた際に次のようなエラーが戻されたとします。

ERROR at line 1:
ORA-31468: cannot process DDL change record
ORA-06512: at "SYS.DBMS_CDC_PUBLISH", line 79
ORA-06512: at line 2

アラート・ログには次のような行が含まれます。

Mon Jun  9 16:13:44 2003
Change Data Capture received DDL for change set PRODUCTS_SET
Change Data Capture received DDL and stopping: truncate table products
Mon Jun  9 16:13:50 2003
Change Data Capture did not process LCR with scn 219777
Streams Apply Server P001 pid=19 OS id=11730 stopped
Streams Apply Reader P000 pid=17 OS id=11726 stopped
Streams Apply Server P000 pid=17 OS id=11726 stopped
Streams Apply Server P001 pid=19 OS id=11730 stopped
Streams AP01 with pid=15, OS id=11722 stopped

TRUNCATE TABLE文は表のすべての行を削除するので、パブリッシャは、チェンジ・データ・キャプチャ処理を再有効化する前に、サブスクライバに通知する必要があります。サブスクリプション・ウィンドウをパージおよび拡張するようサブスクライバに提案する場合もあります。パブリッシャは次に、チェンジ・セットを変更し、remove_ddl => 'Y'パラメータおよびrecover_after_error => 'Y'パラメータを次のように指定して、チェンジ・データ・キャプチャ処理のリストアを試行できます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET(

change_set_name     => 'PRODUCTS_SET',
recover_after_error => 'y',
remove_ddl          => 'y');

END;
/

この手順が完了したら、アラート・ログには次のような行が含まれます。

Mon Jun  9 16:20:17 2003
Change Data Capture received DDL and ignoring: truncate table products
The scn for the truncate statement is 202998

ここで、パブリッシャはチェンジ・セットを有効化する必要があります。TRUNCATE TABLE文の後に発生したすべての変更データは、チェンジ・テーブルに反映されます。この際、変更データが失われることはありません。

同期チェンジ・セットの管理

パブリッシャは、同期チェンジ・セットを有効化または無効化できます。チェンジ・セットが無効化されると、有効化されるまで新しい変更データを処理できません。

同期チェンジ・セットの有効化および無効化

同期チェンジ・セットは常に有効な状態で作成されます。非同期チェンジ・セットは常に無効な状態で作成されます。

パブリッシャは、次のコールでPRODUCTS_SET同期チェンジ・セットを有効化できます。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET(

change_set_name => 'PRODUCTS_SET',
enable_capture  => 'y');

END;
/

パブリッシャは、同じコールを使用(ただし、enable_captureを'n'に設定)してPRODUCTS_SET同期チェンジ・セットを無効化できます。

チェンジ・テーブルの管理

チェンジ・テーブル管理タスクは、チェンジ・テーブルの不要データのパージを除いて、すべてパブリッシャが行います。このタスクを最も効率的に機能させるには、パブリッシャとサブスクライバの両方からのアクションが必要です。

この項では次のトピックについて説明します。

- チェンジ・テーブルの作成

- チェンジ・テーブルの制御列の理解

- TARGET_COLMAP$およびSOURCE_COLMAP$の値の理解

- チェンジ・マーカーの使用

- チェンジ・テーブルへのサブスクライバのアクセスの制御

- チェンジ・テーブルの不要なデータのパージ

- チェンジ・テーブルの削除

チェンジ・テーブルの作成

チェンジ・テーブルを作成する際は、パブリッシャは、次の推奨事項を認識している必要があります。

- チェンジ・データ・キャプチャのすべてのモードで、パブリッシャは、システム表領域にチェンジ・テーブルを作成してはいけません。
次のいずれかの方法を使用して、パブリッシャが管理する表領域にチェンジ・テーブルが作成されるようにできます。最初の方法では、パブリッシャが作成したすべてのチェンジ・テーブルが単一の表領域に作成され、2つ目の方法では、パブリッシャは、各チェンジ・テーブルに対して異なる表領域を指定できます。

- データベース管理者がパブリッシャのアカウントを作成する際に、デフォルト表領域を指定する。次に例を示します。

CREATE USER cdcpub DEFAULT TABLESPACE ts_cdcpub;

- パブリッシャがチェンジ・テーブルを作成する際に、options_stringパラメータを使用して、作成されるチェンジ・テーブルの表領域を指定する。例については、「同期公開の実行」の手順4を参照してください。

両方の方法を使用する場合、options_stringパラメータでパブリッシャが指定した表領域が、SQL文のCREATE USERで指定したデフォルト表領域よりも優先されます。

- 非同期のチェンジ・データ・キャプチャについて、パブリッシャは、DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャで参照されるソース・テーブルが、このプロシージャをコールする前に作成されていることを確認する必要があります。このプロシージャで指定されるチェンジ・セットで、stop_on_ddlパラメータを'Y'に設定している場合は特にそうです。
パブリッシャが、stop_on_ddlパラメータを'Y'に設定してチェンジ・セットを作成した後にチェンジ・テーブルを作成し、次にソース・テーブルが作成されたものと仮定します。このシナリオでは、ソース・テーブルを作成するDDLによって、stop_on_ddl制約が発動され、チェンジ・データ・キャプチャ処理が停止されます。

注意:
パブリッシャはチェンジ・テーブルのパーティション化プロパティを変更しないでください。チェンジ・テーブルのパーティション化は、チェンジ・データ・キャプチャによって、チェンジ・テーブル管理の一部として自動的に管理されます。

- 非同期のチェンジ・データ・キャプチャについては、ソース・データベースDBAは、チェンジ・テーブルでキャプチャされるすべてのソース・テーブル列に対して、無条件のログ・グループを作成する必要があります。これは、ソース・テーブルにチェンジ・テーブルが作成される前に行う必要があります。キャプチャされるソース・テーブル列に対して無条件のログ・グループを作成しない場合、更新DML操作が発生する際に、チェンジ・テーブル内の変更されていないユーザー列値の一部が、実際のソース・テーブル値を反映せずにNULLとなります。実際にNULLである列値と変更されていない列値を区別するには、パブリッシャがTARGET_COLMAP$制御列を評価する必要があります。無条件のログ・グループの作成については、「非同期チェンジ・データ・キャプチャとサプリメンタル・ロギング」を、制御列については、「チェンジ・テーブルの制御列の理解」を参照してください。

チェンジ・テーブルの制御列の理解

チェンジ・テーブルは、データベース表に格納されている変更データ自体と、制御列が含まれるチェンジ・テーブルのメンテナンスに必要なシステム・メタデータで構成されています。

表17-11に、チェンジ・テーブルの制御列を示します。表には、列名、データ型、モード、列がオプションかどうか、および説明が含まれます。

モードは、列に関連付けられたチェンジ・データ・キャプチャのタイプを示します。値Allは、列が同期モードおよび非同期チェンジ・データ・キャプチャのすべてのモードに関連付けられていることを示します。同期および非同期チェンジ・データ・キャプチャの両方のモードについて、サブスクライバがサブスクライバ・ビューを問い合せてDML変更を発生した順番で戻す場合、その問合せでは、CSCN$およびRSID$の順でデータを整列する必要があります。

制御列は、パブリッシャがチェンジ・テーブルから除外することを選択できる場合はオプションとみなされます。表17-11では、どのモードでどの制御列がオプションとなるかを示しています。オプションの制御列はすべてDBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャのパラメータで指定されます。DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLEプロシージャの構文については、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

表17-11 チェンジ・テーブルの制御列

列

データ型

モード

オプション列

説明

OPERATION$

CHAR(2)

All

不可

この列に指定できる値は次のいずれかです。脚注1

I: この行が挿入操作を表すことを示します。

UO: この行が、次の場合について、更新されたソース・テーブル行のビフォア・イメージを表すことを示します。

- 非同期チェンジ・データ・キャプチャ

- 主キー・ベースのオブジェクトIDがチェンジ・テーブルに含まれ、主キーではないキャプチャされた列が変更される際の同期チェンジ・データ・キャプチャ

UU: UOによって示される行を除いて、この行が、同期チェンジ・データ・キャプチャの更新されたソース・テーブル行のビフォア・イメージを表すことを示します。

UN: この行が、更新されたソース・テーブル行のアフター・イメージを表すことを示します。

D: この行が削除操作を表すことを示します。

CSCN$

NUMBER

All

不可

同期CDCの場合は、EXTEND_WINDOW操作で使用する値。非同期CDCの場合は、DML操作で使用する値。

DDLDESC$

CLOB

非同期

可能

実行された実際のDDL文を含むCLOB。

DDLPDOBJN$

NUMBER

非同期

可能

このリリースでは使用されません。

DDLOPER$

NUMBER

非同期

可能

実行されたDDL操作の種類を示すビット・ベクトル。

RSID$

NUMBER

All

可能

このトランザクション内の一意の行順序ID。脚注2 RSID$列は、1つのトランザクション内では操作のキャプチャ順序を反映しますが、複数のトランザクションにわたる順序は反映しません。パブリッシャは、RSID$列値を単独で使用しても、トランザクション間のコミット済操作を整列できません。そのためには、CSCN$列値と組み合せて使用する必要があります。

SOURCE_COLMAP$

RAW(128)

同期

可能

ソース・テーブル内の更新済列のビット・マスク脚注3。

TARGET_COLMAP$

RAW(128)

All

可能

チェンジ・テーブル内の更新済列のビット・マスク脚注3。

COMMIT_TIMESTAMP$

DATE

All

不可

このトランザクションのコミット時刻。

TIMESTAMP$

DATE

All

可能

ソース・テーブルで操作が発生した時刻。

USERNAME$

VARCHAR2(30)

All

可能

操作を実行したユーザーの名前。リリース10.2より前のOracle Databaseで作成されたチェンジ・ソースの場合、値は常にNULLです。

ROW_ID$

ROW_ID

All

可能

ソース・テーブル内で影響を受ける行のROWID。リリース10.2より前のOracle Databaseで作成されたチェンジ・ソースの場合、値は常にNULLです。

XIDUSN$

NUMBER

非同期

不可

トランザクションIDのUNDOセグメント番号。

XIDSLT$

NUMBER

非同期

不可

トランザクションIDのスロット番号。

XIDSEQ$

NUMBER

非同期

不可

トランザクションIDの順序番号。

SYS_NC_OID$

RAW(16)

同期

可能

オブジェクトID。

脚注1この列に基づいた問合せを指定する場合、IまたはD列値をそれぞれ「I」、「D」として指定します。OPERATION$列は2文字列で、値は左揃えおよび空白詰めです。「I」または「D」の値を指定する問合せでは、値は戻されません。

脚注2RSID$列を使用して、特定の操作のアフター・イメージとビフォア・イメージを関連付けることができます。特定の更新操作について、アフター・イメージのRSID$列の値は常に、ビフォア・イメージのRSID$列値の値と一致します。

脚注3ビットマスクは、行のどの列が更新されているかを示すバイナリ値の配列です。

TARGET_COLMAP$およびSOURCE_COLMAP$の値の理解

TARGET_COLMAP$およびSOURCE_COLMAP$列は、行のどの列が変更されたかを示すために使用します。TARGET_COLMAP$列は、チェンジ・テーブル行のどの列が変更されたかを示します。SOURCE_COLMAP$列(同期チェンジ・テーブルにのみ含まれる)は、ソース・テーブル行のどの列が変更されたかを示します。

TARGET_COLMAP$およびSOURCE_COLMAP$列のデータ型はRAW(128)なので、各列には128バイトのバイナリ情報を格納できます。このバイナリ情報は、左側の下位バイトと右側の上位バイトが連なったワードから構成されます。デフォルトでは、フィールドが表示される際に、システムによって各バイトが解釈され、それぞれの値が表示されます。例17-1に、TARGET_COLMAP$のサンプル値を示します。例の後でこのデータの解釈方法を説明します。

例17-1 TARGET_COLMAP$のサンプル値

FE110000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000

例17-1の最初の「FE」が下位バイトで、最後の「00」が上位バイトです。値の意味を正しく解釈するには、各バイトに設定されているビットを考察する必要があります。ビットマップのビットは、0から開始してカウントされます。最初のビットはビット0、2つ目のビットはビット1などとなります。ビット0は常に無視されます。その他のビットについては、特定のビットが1に設定される場合、その列の値が変更されていることを意味します。

例17-1で示したバイト列を解釈するには、左から右に読む必要があります。最初のバイトは文字列「FE」です。左から右にビットに分解すると、この文字列は「1111 1110」となり、チェンジ・テーブルの列「7,6,5,4 3,2,1,-」(ハイフンは無視されたビットを表す)にマップされます。最初のビットは、チェンジ・テーブルの列7が変更されたかどうかを示します。最も右側のビットは無視されています。例17-1のこれらの値は、最初の7列に値があることを示しています。チェンジ・テーブル内の最初のいくつかの列は制御列であるため、これは典型的なことです。

例17-1の次のバイトは文字列「11」です。ビットに分解すると、この文字列は「0001 0001」で、チェンジ・テーブルの列「15,14,13,12 11,10,9,8」にマップされます。これらのビットでは、列8と12が変更されたことを示します。列9、10、11、13、14、15は変更されていません。残りの文字列はすべて「00」で、他の列がすべて変更されていないことを示しています。

パブリッシャは、次の問合せを発行して、列数の列名へのマッピングを割り出すことができます。

SELECT COLUMN_NAME, COLUMN_ID FROM ALL_TAB_COLUMNS
WHERE OWNER='PUBLISHER_STEWART' AND TABLE_NAME='MY_CT';

COLUMN_NAME                    COLUMN_ID
------------------------------ ----------
OPERATION$                           1
CSCN$                                2
COMMIT_TIMESTAMP$                    3
XIDUSN$                              4
XIDSLT$                              5
XIDSEQ$                              6
RSID$                                7
TARGET_COLMAP$                       8
C_ID                                 9
C_KEY                                10
C_ZIP                                11

COLUMN_NAME                     COLUMN_ID
------------------------------ ----------
C_DATE                               12
C_1                                  13
C_3                                  14
C_5                                  15
C_7                                  16
C_9                                  17

例17-1を使用すると、パブリッシャは、OPERATION$、CSCN$、COMMIT_TIMESTAMP$、XIDUSN$、XIDSLT$、XIDSEQ$、RSID$、TARGET_COLMAP$およびC_DATEの列が、このTARGET_COLMAP$の値によって表されるチェンジ・テーブルの特定の変更行で変更されたことがわかります。

チェンジ・データ・キャプチャでは、すべての変更行のすべての制御列の値が生成されるので、制御列に対応するビットは、各TARGET_COLMAP$列で常に1に設定されることに注意してください。OPERATION$列の値がUNおよびIの場合、変更されたユーザー列に対応するビットは、必要に応じて1に設定されます。OPERATION$列の値については、表17-11を参照してください。また、非同期CDCを使用した場合、UPDATE SET句に指定した列は、値が実際に元の値から変更されたかどうかにかかわらず必ず1になることに注意してください。同期CDCの場合は、値が元の値から変更された場合にかぎり1になります。

TARGET_COLMAP$列の値は、一般的にチェンジ・テーブルのNULL値の意味の判別に使用します。チェンジ・テーブルの列値は次の2つの理由からNULLとなります。ユーザーまたはアプリケーションによって値がNULLに変更された場合と、ソース・テーブルからのREDOデータに値が存在していなかったため、チェンジ・データ・キャプチャによってNULL値が列に挿入された場合です。ユーザーが値をNULLに変更した場合、その列のビットは1に設定され、チェンジ・データ・キャプチャによって値がNULLに設定された場合は、列は0に設定されます。

SOURCE_COLMAP$列の値も同様の方法で解釈されますが、次の例外があります。

- SOURCE_COLMAP$列は、チェンジ・テーブルの列ではなく、ソース・テーブルの列を指す。

- SOURCE_COLMAP$列は、制御列がソース・テーブルにないため、制御列を参照しない。

- OPERATION$列の値がUO、UU、UNおよびIの場合、SOURCE_COLMAP$列において、変更されたソース列は1に設定される。OPERATION$列の値については、表17-11を参照してください。

- SOURCE_COLMAP$列は、同期チェンジ・テーブルに対してのみ有効である。

チェンジ・マーカーの使用

次の例は、チェンジ・マーカーの使用方法を示しています。まず、チェンジ・セットを作成します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_SET (
CHANGE_SET_NAME     => 'async_set1',
CHANGE_SOURCE_NAME  => 'HOTLOG_SOURCE',
STOP_ON_DDL         => 'N',
BEGIN_DATE          => SYSDATE,
END_DATE            => SYSDATE+5);

PL/SQL procedure successfully completed

次に、チェンジ・テーブルを作成します。ここでは、3つのDDLマーカー(ddloper$、ddldesc$、ddlpdobjn$)を使用します。

BEGIN
DBMS_CDC_PUBLISH.CREATE_CHANGE_TABLE (
OWNER             => 'tcdcpub',
CHANGE_TABLE_NAME => 'cdc_psales_act',
CHANGE_SET_NAME   => 'async_set1',
SOURCE_SCHEMA     => 'tcdcpub',
SOURCE_TABLE      => 'cdc_psales',
COLUMN_TYPE_LIST  => 'PROD_IC NUMBER(6)
NUMBER, TIME_ID DATE',
CAPTURE_VALUES    => 'both',
RS_ID             => 'N',
ROW_ID            => 'N',
USER_ID           => 'N',
TIMESTAMP         => 'N',
OBJECT_ID         => 'N',
SOURCE_COLMAP     => 'N',
TARGET_COLMAP     => 'Y',
OPTIONS_STRING    => NULL,
DDL_MARKERS       => 'Y');
END;
/

PL/SQL procedure successfully completed.
describe cdc_psales_act;

Name                    Null?     Type
---------------------------------------
OPERATION$                        CHAR(2)
CSCN$                             NUMBER
COMMIT_TIMESTAMP                  DATE
XIDUSN$                           NUMBER
XIDSLT$                           NUMBER
XIDSEQ$                           NUMBER
TARGET_COLMAP$                    RAW(128)
DDLOPER$                          NUMBER
DDLDESC$                          CLOB
DDLPDOBJN$                        NUMBER
PROD_D                            NUMBER(6)
CUST_ID                           NUMBER
TIME_ID                           DATE

次に、このチェンジ・セットのキャプチャを有効にします。

BEGIN
DBMS_CDC_PUBLISH.ALTER_CHANGE_SET (
CHANGE_SET_NAME  => 'asynch_set1',
ENABLE_CAPTURE   => 'Y');
END;
.
PL/SQL procedure successfully completed

最後にDDL文を発行し、キャプチャが実行されたかどうか確認します。

ALTER TABLE cdc_psales DROP PARTITION Dec_06;

SELECT ddloper$, DECODE(ddloper$, NULL, 'NULL',
DBMS_CDC_PUBLISH.GET_DDLOPER(ddloper$))
AS DDL_OPER
FROM cdc_psales_act
WHERE DDLOPER$ IS NOT NULL
ORDER BY cscn$;

ddloper$            DDL_OPER
---------------------------------
512                 Drop Partition
1 row selected.

SELECT ddldesc$
FROM cdc_psales_act
WHERE ddloper$
IS NOT NULL
ORDER BY cscn;

DDLDESC$
------------------------
alter table cdc_psales drop partition Dec_06
1 row selected.

チェンジ・テーブルへのサブスクライバのアクセスの制御

パブリッシャは、サブスクライバに権限を付与して、サブスクライバがチェンジ・テーブルにアクセスできるようにします。ソース・テーブルに対する権限はチェンジ・テーブルには適用されません。そのため、サブスクライバは、ソース・テーブルに対してSELECT操作を実行する権限は所有していても、そのソース・テーブルを参照するチェンジ・テーブルに対してSELECT操作を実行する権限を所有していない場合があります。

パブリッシャは、SQL文のGRANTおよびREVOKEを実行し、ユーザーやロールに対してチェンジ・テーブルへのSELECT権限の付与と取消しを行うことで、サブスクライバによる変更データへのアクセスを制御します。パブリッシャは、サブスクライバがチェンジ・テーブルにサブスクライブできるように、事前にSELECT権限を付与する必要があります。

また、パブリッシャは、チェンジ・テーブルのサブスクライバに(INSERT、UPDATE、DELETE文を使用する)いかなるDMLアクセス権も与えてはいけません。サブスクライバがチェンジ・テーブル内のデータを意図せずに変更してしまった場合、ソースとの不整合が発生する可能性があるためです。さらに、パブリッシャは、サブスクライバがDMLアクセス権を持っているスキーマには、チェンジ・テーブルを作成しないようにする必要があります。

チェンジ・テーブルの不要なデータのパージ

この項ではパージ操作について説明します。パージ操作で最高の結果を得るには、サブスクライバ側の行為が必要となります。各サブスクライバは、変更データの使用を完了したらそのことを知らせます。その後、チェンジ・テーブルでサブスクライバが使用しなくなったデータを、チェンジ・データ・キャプチャまたはパブリッシャが次のように実際に削除(パージ)します。

- サブスクライバ
変更データの使用が完了したら、サブスクライバはDBMS_CDC_SUBSCRIBE.PURGE_WINDOWプロシージャをコールする必要があります。これによって、このサブスクライバがその変更データを必要としなくなったことが、チェンジ・データ・キャプチャおよびパブリッシャに示されます。DBMS_CDC_SUBSCRIBE.PURGE_WINDOWプロシージャを実行してもチェンジ・テーブルから物理的に行が削除されるわけではありませんが、データはサブスクライバ・ビューから論理的に削除され、選択できなくなります。

さらに、「変更データのサブスクライブ」に示すように、不要なサブスクリプションを削除するには、サブスクライバはDBMS_CDC_SUBSCRIBE.DROP_SUBSCRIPTIONプロシージャをコールする必要があります。

DBMS_CDC_SUBSCRIBE.DROP_SUBSCRIPTIONプロシージャおよびDBMS_CDC_SUBSCRIBE.PURGE_WINDOWプロシージャの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

- チェンジ・データ・キャプチャ
チェンジ・データ・キャプチャでは、DBMS_SCHEDULERパッケージ(最初のチェンジ・テーブルを作成したパブリッシャのアカウントで実行する)を使用してパージ・ジョブが作成されます。このパージ・ジョブによってDBMS_CDC_PUBLISH.PURGEプロシージャがコールされ、サブスクライバがチェンジ・テーブルで使用しなくなったデータが削除されます。このジョブの名前はcdc$_default_purge_jobです。デフォルトでは、このジョブは24時間ごとに実行されます。このジョブのスケジュールは、DBMS_SCHEDULER.SET_ATTRIBUTEを使用してrepeat_interval属性を設定することにより変更できます。DBMS_SCHEDULERパッケージではその他の属性を確認したり変更することもできます。

これによって、チェンジ・テーブルのサイズが無制限に大きくならないことが保証されます。DBMS_CDC_PUBLISH.PURGEプロシージャへのコールでは、アクティブなサブスクリプション・ウィンドウがすべて評価され、どの変更データがまだ必要であるかが判別されます。アクティブなサブスクリプション・ウィンドウで、1つ以上のサブスクライバによって参照されている可能性のあるデータはパージされません。

DBMS_SCHEDULERパッケージについては、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

- パブリッシャ
パブリッシャはいつでもパージ操作を手動で実行できます。パブリッシャは、チェンジ・データ・キャプチャによって行われる自動パージ操作よりも、詳細にパージ操作を実行できます。パブリッシャが使用できるパージ操作には次の3つがあります。

- DBMS_CDC_PUBLISH.PURGE
ステージング・データベース上のすべてのチェンジ・テーブルをパージします。これは、チェンジ・データ・キャプチャによって自動的に行われるPURGE操作と同じです。

- DBMS_CDC_PUBLISH.PURGE_CHANGE_SET
指定のチェンジ・セットのチェンジ・テーブルをすべてパージします。

- DBMS_CDC_PUBLISH.PURGE_CHANGE_TABLE
指定のチェンジ・テーブルをパージします。

したがって、サブスクライバによるDBMS_CDC_SUBSCRIBE.PURGE_WINDOWプロシージャへのコールとチェンジ・データ・キャプチャによるPURGEプロシージャへのコール(またはパブリッシャによるいずれかのPURGEプロシージャへのコール)は一緒に機能します。つまり、各サブスクライバがサブスクリプション・ウィンドウをパージする際、不要になった変更データが示され、実際にデータをパージする前に、すべてのサブスクライバからの入力全体がPURGEプロシージャによって評価されます。表をパージする場合はPURGE_CHANGE_TABLEプロシージャを、チェンジ・セットのすべてのチェンジ・テーブルをパージする場合はPURGE_CHANGE_SETプロシージャを使用します。

forceがNに設定されている場合、Oracle Databaseは最も効率的な方法で(表ロックを使用して)パージしようとします。ただしパージは保証されません。forceをYに設定すると、このパージは保証されます。

サブスクライバがPURGE_WINDOWのコールに失敗する可能性もあるので注意してください。失敗した場合は、不要な行がパージ・ジョブによって削除されません。パブリッシャは、必要に応じてDBMS_CDC_PUBLISH.DROP_SUBSCRIPTIONプロシージャを使用してアクティブなサブスクリプションを削除できますが、サブスクライバが変更データを使用している可能性があることを第一に考慮する必要があります。極端な場合には、パブリッシャがアクティブなサブスクリプションを手動で削除して領域を再利用できるようにすることもあります。そのような事態の一例は、サブスクライバが、適切な場合にPURGE_WINDOWプロシージャのコールに失敗するアプリケーション・プログラムである場合です。パブリッシャは、必要に応じてDBMS_CDC_PUBLISH.DROP_SUBSCRIPTIONプロシージャを使用してアクティブなサブスクリプションを削除できますが、サブスクライバが変更データを使用している可能性があることを第一に考慮する必要があります。

DBMS_CDC_PUBLISH.PURGEを使用する前にDBMS_CDC_SUBSCRIBE.EXTEND_WINDOWを実行することが重要です。これによって、データを保護し、サブスクライバが必要とするデータをパブリッシャに認識させることができます。EXTEND_WINDOWプロシージャを実行しない場合、パージ・ジョブは期待どおりに動作します。つまり、サブスクライバでデータが使用されるかどうかにかかわらず、すべてのデータがパージされます。

チェンジ・テーブルの削除

チェンジ・テーブルを削除するには、パブリッシャは、DBMS_CDC_PUBLISH.DROP_CHANGE_TABLEプロシージャをコールする必要があります。このプロシージャは、チェンジ・テーブル自体とそのチェンジ・データ・キャプチャ・メタデータの両方を確実に削除します。パブリッシャがチェンジ・テーブルにSQL文のDROP TABLEを使用しようとすると、次のエラーが発生して失敗します。

ORA-31496 must use DBMS_CDC_PUBLISH.DROP_CHANGE_TABLE to drop change tables

DBMS_CDC_PUBLISH.DROP_CHANGE_TABLEプロシージャは、チェンジ・テーブルを使用中のアクティブなサブスクライバがある間に、パブリッシャが不注意でそのチェンジ・テーブルを削除しないための保護対策でもあります。サブスクリプションがアクティブな間にDBMS_CDC_PUBLISH.DROP_CHANGE_TABLEがコールされると、このプロシージャは次のエラーで失敗します。

ORA-31424 change table has active subscriptions

アクティブなサブスクリプションがあっても、チェンジ・テーブルを削除する必要がある場合、パブリッシャはforce_flag => 'Y'パラメータを使用してDROP_CHANGE_TABLEプロシージャをコールします。これは、通常の安全策を無効にし、アクティブなサブスクリプションがある場合でもチェンジ・テーブルを削除できるようチェンジ・データ・キャプチャに通知します。サブスクリプションは有効でなくなり、サブスクライバは変更データにアクセスできなくなります。

注意:
DROP USER CASCADE文は、パブリッシャのすべてのチェンジ・テーブルを削除するため、その(削除された)チェンジ・テーブルに対して他のユーザーがアクティブなサブスクリプションを持っていた場合、それらは有効ではなくなります。DROP USER CASCADE文は、チェンジ・テーブルの削除に加え、DROP USER CASCADE文で指定したユーザーが所有するすべてのサブスクリプション、チェンジ・セットおよびチェンジ・ソースを削除します。

Oracle Data Pumpを使用したチェンジ・データ・キャプチャ・オブジェクトのエクスポートおよびインポート

Oracle Data Pumpは、チェンジ・データ・キャプチャで使用できるエクスポートおよびインポート・ユーティリティです。

以降の項では、チェンジ・データ・キャプチャでOracle Data Pumpを使用する際の制限事項、例およびパブリッシャの考慮事項を示します。

- チェンジ・データ・キャプチャでOracle Data Pumpを使用する際の制限事項

- Oracle Data Pumpのエクスポートおよびインポートのコマンド例

- チェンジ・テーブルのエクスポートおよびインポートにおけるパブリッシャの考慮事項

- インポート操作後のAutoLogチェンジ・データ・キャプチャ・オブジェクトの再作成

チェンジ・データ・キャプチャでOracle Data Pumpを使用する際の制限事項

チェンジ・データ・キャプチャのチェンジ・ソース、チェンジ・セット、チェンジ・テーブルおよびサブスクリプションは、Oracle Data Pumpのexpdpおよびimpdpコマンドを使用してエクスポートおよびインポートされます。これには次の制限事項があります。

- チェンジ・データ・キャプチャ・オブジェクトは、データベース全体のエクスポートおよびインポート操作(expdpおよびimpdbコマンドでFULL=yパラメータを指定した操作)の一部としてのみエクスポートおよびインポートされます。スキーマ・レベルのインポートおよびエクスポート操作には、基底のオブジェクトの一部(チェンジ・テーブルの基になる表など)が含まれますが、変更データのキャプチャに必要なチェンジ・データ・キャプチャのメタデータは含まれません。

- チェンジ・データ・キャプチャ・オブジェクトのインポートを実行するユーザーは、次の権限が必要です。

- CREATE SEQUENCEまたはCREATE ANY SEQUENCE
インポートを実行するユーザーが元のパブリッシャである場合は、CREATE SEQUENCEで十分です。

- ALTER DATABASE

- EXP_FULL_DATABASEまたはIMP_FULL_DATABASE
PUBLICによるインポート・パッケージへのアクセスは削除されました。アクセスできるのは、EXP_FULL_DATABASEまたはIMP_FULL_DATABASE権限を持つユーザーのみです。

- AutoLogのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルはサポートされていません。「インポート操作後のAutoLogチェンジ・データ・キャプチャ・オブジェクトの再作成」を参照してください。

- 非同期のチェンジ・セットおよびチェンジ・テーブルのエクスポートは、ユーザーがエクスポート対象のデータベースにDDLおよびDML変更を加えていない時に行う必要があります。

- 非同期のチェンジ・セットおよびチェンジ・テーブルをインポートする際は、基底のOracle Streams構成もインポートする必要があります。それには、Oracle Data Pumpのインポート・パラメータSTREAMS_CONFIGURATIONを明示的にyに設定し(または暗黙的にデフォルトを受け入れて)、必要なStreamsオブジェクトがインポートされるようにします。インポート操作を実行し、STREAMS_CONFIGURATION=nを指定する場合、インポートされた非同期のチェンジ・セットおよびチェンジ・テーブルでは、変更データのキャプチャが続行できなくなります。

- チェンジ・データ・キャプチャ・オブジェクトは、インポートされる際に既存のオブジェクトを上書きすることはありません(表に対するインポート・コマンドのTABLE_EXISTS_ACTION = skipパラメータの効果と似ています)。チェンジ・データ・キャプチャでは、これらの場合について、インポート・ログに警告が生成されます。

- チェンジ・データ・キャプチャ・オブジェクトは、すべての基底オブジェクトが予定どおり正常な形式で存在しているかどうかを判別するためにインポート操作の最後に検証されます。チェンジ・データ・キャプチャでは、検証の問題が検出されると、インポート・ログに検証の警告が生成されます。検証の警告を伴ってインポートされたチェンジ・データ・キャプチャ・オブジェクトは通常、変更データのキャプチャを続行できません。

Oracle Data Pumpのエクスポートおよびインポートのコマンド例

次に、チェンジ・データ・キャプチャ・オブジェクトをサポートする、データ・ポンプ・エクスポート・コマンドとインポート・コマンドの例を示します。

> expdp DIRECTORY=dpump_dir FULL=y
> impdp DIRECTORY=dpump_dir FULL=y STREAMS_CONFIGURATION=y

Oracle Data Pumpについては、『Oracle Databaseユーティリティ』を参照してください。

チェンジ・テーブルのエクスポートおよびインポートにおけるパブリッシャの考慮事項

チェンジ・テーブルをエクスポートおよびインポートする際のパブリッシャの考慮事項を次に示します。

- チェンジ・テーブルのインポート時には、ジョブ・キュー内でチェンジ・データ・キャプチャのパージ・ジョブの有無がチェックされます。パージ・ジョブが見つからない場合は、(DBMS_CDC_PUBLISH.PURGEプロシージャを使用して)自動的にパージ・ジョブが発行されます。チェンジ・テーブルがインポートされても、パージ・ジョブの実行時(デフォルトでは24時間後)までにサブスクリプションが発生しなければ、その表のすべての行がパージされます。
パブリッシャは、次のいずれかの方法を選択し、チェンジ・テーブルからのデータのパージを防止できます。

- DBMS_SCHEDULERパッケージを使用してパージ・ジョブを一時停止し、ジョブを(STOP_JOBプロシージャを使用して)使用禁止にするか、将来サブスクリプションが存在するようになった時点で(SET_ATTRIBUTEプロシージャを使用して)ジョブを実行します。

注意:
パージ・ジョブを無効にする場合は、サブスクリプションがアクティブになった後にリセットするのを忘れないようにする必要があります。これにより、チェンジ・テーブルが無制限に大きくなるのを防止できます。

- 一時的なサブスクリプションを作成して、チェンジ・テーブルのデータを実際のサブスクリプションが行われるまで保持します。その後で、一時的なサブスクリプションを削除します。

- チェンジ・テーブルがすでに存在するソース・テーブルにデータをインポートすると、インポートしたデータも関連するチェンジ・テーブルに記録されます。
チェンジ・テーブルct_salesが対応付けられているソース・テーブルSALESをパブリッシャが所有しているとします。パブリッシャがSALESにデータをインポートすると、そのデータはct_salesにも記録されます。

- オプションのROW_ID制御列を持つチェンジ・テーブルをインポートすると、チェンジ・テーブルに格納されているROW_ID列が意味を持つのは、関連するソース・テーブルがインポートされていない場合のみです。ソース・テーブルが再作成されるかインポートされると、各行にはチェンジ・テーブルに以前に記録されていたROW_IDとは無関係な新規ROW_IDが割り当てられます。

Oracle9iデータベースで使用できる元のレベルのエクスポートおよびインポートのサポートは、下位互換性のために保持されています。SYNC_SETチェンジ・セットにある同期チェンジ・テーブルは、全データベース、スキーマまたは個々の表のエクスポート操作の一部としてエクスポートでき、必要に応じてインポートできます。チェンジ・データ・キャプチャ・オブジェクト(チェンジ・ソース、チェンジ・セット、SYNC_SETチェンジ・セットにないチェンジ・テーブル、サブスクリプション)は、元のエクスポートおよびインポートのサポート対象外です。

インポート操作後のAutoLogチェンジ・データ・キャプチャ・オブジェクトの再作成

AutoLogチェンジ・データ・キャプチャ・オブジェクトを格納しているデータベースに対して、データ・ポンプの全データベース・インポート操作が完了したら、次の手順を実行して、これらのオブジェクトをリストアする必要があります。

1. パブリッシャは、AutoLogチェンジ・データ・キャプチャ・オブジェクトの基になるデータベース・オブジェクトを手動で削除する必要があります。

2. パブリッシャは、適切なDBMS_CDC_PUBLISHプロシージャを使用して、AutoLogのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルを再作成する必要があります。

3. サブスクライバは、AutoLogチェンジ・セットに対してサブスクリプションを再作成する必要があります。

AutoLogチェンジ・データ・キャプチャのオブジェクトが関与するデータ・ポンプの全データベース・エクスポート操作と、前述の手順におけるデータ・ポンプの全データベース・インポート操作後の再作成との合間において、変更データが失われる場合があります。この合間でソース・テーブルに対する変更をできるだけ防止することによって、この可能性を最小化できます。

データ・ポンプ・インポート操作後にAutoLogチェンジ・データ・キャプチャ構成を再作成する前に、基底のオブジェクト(チェンジ・テーブルの基になる表、サブスクライバ・ビュー、チェンジ・セットで使用される順序、Streamsの適用プロセス、Streamsのキューおよびキュー表)を最初に削除する必要があります。表17-12に、基底の各オブジェクトおよびデータ・ポンプ・インポート操作後にそれらのオブジェクトを削除する方法を示します。

表17-12 データ・ポンプ・インポート操作後にオブジェクトを削除する方法

オブジェクト

オブジェクトを削除するSQL文またはPL/SQLパッケージ

表

SQL DROP TABLE文

サブスクライバ・ビュー

SQL DROP VIEW文

チェンジ・セットで使用される順序

SQL DROP SEQUENCE文

Streams適用プロセス

PL/SQL DBMS_APPLY_ADM.DROP_APPLY()パッケージ

Streamsのキュー

PL/SQL DBMS_AQADM.DROP_QUEUE()パッケージ

Streamsのキュー表

PL/SQL DBMS_AQADM.DROP_QUEUE_TABLE()パッケージ

チェンジ・セットで使用される順序の名前を取得するには、CDC$で始まり、少なくともチェンジ・セット名の頭文字が含まれる順序名についてALL_SEQUENCESビューを問い合せます。

Streamsオブジェクトの名前を取得するには、CDC$で始まり、少なくともチェンジ・セット名の頭文字が含まれる名前について、DBA_APPLY、DBA_QUEUESおよびDBA_QUEUE_TABLESビューを問い合せます。

パブリッシャが変更を行う際のサブスクリプションへの影響

チェンジ・データ・キャプチャ環境は動的です。パブリッシャは、チェンジ・テーブルを随時追加または削除できます。また、必要に応じて既存のチェンジ・テーブルに列を追加したり、そこから列を削除できます。次のリストでは、チェンジ・データ・キャプチャ環境に対する変更がサブスクリプションに与える影響について説明します。

- パブリッシャが、新規のチェンジ・テーブルを追加したり、既存のチェンジ・テーブルに列を追加したりしても、サブスクライバには明示的に通知されません。サブスクライバは、ALL_PUBLISHED_COLUMNSビューをチェックして、新規の列が追加されたかどうかや、その列へのアクセス権が付与されているかどうかを確認できます。

- 表17-13に、パブリッシャがチェンジ・テーブルに列を追加した際の影響を示します。

表17-13 パブリッシャがチェンジ・テーブルに列を追加することによる影響

パブリッシャが追加するもの

条件 . .

結果 . .

ユーザー列

新しいサブスクリプションにこの列が含まれる

このサブスクリプションのサブスクリプション・ウィンドウが、列が追加された点から始まります。

ユーザー列

新しいサブスクリプションに、この新しく追加した列が含まれない

このサブスクリプションのサブスクリプション・ウィンドウは、変更データが最初に使用できる時点から始まります。新しい列は表示されません。

ユーザー列

サブスクリプションが存在する

このサブスクリプションのサブスクリプション・ウィンドウが変更されないまま残ります。

制御列

新しいサブスクリプションが作成される

このサブスクリプションのサブスクリプション・ウィンドウは、変更データが最初に使用できる時点から始まります。サブスクリプションでは、制御列を即時に参照できます。制御列を追加する前に存在していたチェンジ・テーブルのすべての行では、新規に追加された制御列の値がNULLになります。

制御列

サブスクリプションが存在する

このサブスクリプションでは、サブスクリプション・ウィンドウの低位限界が制御列が追加された時点を超えるようにサブスクリプション・ウィンドウをパージする(DBMS_CDC_PUBLISH.PURGE_WINDOWプロシージャ)と、新しい制御列を参照できます。

同期チェンジ・データ・キャプチャに関する考慮事項

以降の項では、チェンジ・データ・キャプチャの同期モードを使用する際に、パブリッシャが注意すべき情報を取り上げます。

- ダイレクト・パス・インサートの制限

- 同期チェンジ・データ・キャプチャでサポートされるデータ型と表構造

- ごみ箱からソース・テーブルをリストアする際の制限

ダイレクト・パス・インサートの制限

同期チェンジ・データ・キャプチャは、ダイレクト・パス・インサート文(および、関連するMERGE文およびINSERT文のmulti_table_insert句)の機能をサポートしていません。

パブリッシャが同期モードでチェンジ・テーブルを作成する際、チェンジ・データ・キャプチャは、ソース・テーブルに対してトリガーを作成します。ダイレクト・パス・インサート文は、データベース・トリガーをすべて無効にするため、パラレルDMLモードでダイレクト・パス・インサート用のSQL文を使用してソース・テーブルに挿入された行は、チェンジ・テーブルにキャプチャされません。

同様に、チェンジ・データ・キャプチャは、マルチテーブルのインサート操作およびマージ操作から挿入された行をキャプチャできません。これらの文はダイレクト・パス・インサート文を使用するためです。また、ダイレクト・パス・インサート文では、チェンジ・データ・キャプチャで使用されるトリガーが起動されなかったことを示すエラー・メッセージは戻されません。

ダイレクト・パス・インサート文およびトリガーの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

同期チェンジ・データ・キャプチャでサポートされるデータ型と表構造

同期チェンジ・データ・キャプチャでは、次のものを除く、Oracleのすべての組込みデータ型の列をサポートしています。

- BFILE

- BLOB

- CLOB

- LONG

- NCLOB

- ROWID

- UROWID

- オブジェクト・タイプ(XMLTypeなど)

仮想列はサポートされません。さらに、チェンジ・データ・キャプチャでは、結果として引用符付き識別子の使用はサポートされず、チェンジ・データ・キャプチャの予約キーワードの使用もサポートされません。予約キーワードのリストについては、『Oracle Database SQL言語リファレンス』を参照してください。

同期チェンジ・データ・キャプチャでは、次の表構造をサポートしません。

- 一時表であるソース・テーブル

- オブジェクト表であるソース・テーブル

- サポートされていないデータ型の列(LOB列を含む)またはオーバーフロー・セグメントを持つ索引構成表

ごみ箱からソース・テーブルをリストアする際の制限

同期チェンジ・テーブルのソース・テーブルを削除した後にごみ箱からリストアすると、そのチェンジ・テーブルには変更がキャプチャされなくなります。パブリッシャは、リストアしたソース・テーブルに対する今後の変更をキャプチャするために、新しい同期チェンジ・テーブルを作成する必要があります。

非同期チェンジ・データ・キャプチャに関する考慮事項

以降の項では、チェンジ・データ・キャプチャの非同期モードを使用する際に、パブリッシャとソースおよびステージング・データベースDBAが注意すべき情報を提供します。

- 非同期チェンジ・データ・キャプチャおよびREDOログ・ファイル

- 非同期チェンジ・データ・キャプチャとサプリメンタル・ロギング

- 非同期チェンジ・データ・キャプチャとOracle Streamsコンポーネント

- 非同期チェンジ・データ・キャプチャでサポートされるデータ型と表構造

- NOLOGGINGおよびUNRECOVERABLE操作の制限

非同期チェンジ・データ・キャプチャおよびREDOログ・ファイル

チェンジ・データ・キャプチャの非同期モードでは、REDOログ・ファイルが使用されます。

- HotLog
非同期のHotLogおよび分散HotLogでは、可能な場合はソース・データベースのオンラインREDOログ・ファイルが読み込まれ、それ以外の場合は、アーカイブREDOログ・ファイルが読み込まれます。

- AutoLog
非同期AutoLogチェンジ・データ・キャプチャでは、REDO転送サービスによってソース・データベースからステージング・データベースにコピーされたREDOログ・ファイルが読み込まれます。

AutoLogオンライン・オプションを使用する際は、宛先属性をLGWR ASYNCに指定する必要があります。REDO転送サービスは、ソース・データベースのオンラインREDOログから、ステージング・データベースのスタンバイREDOログにREDOデータをコピーします。チェンジ・データ・キャプチャが変更データを取得するのは、ソース・データベースのトランザクションがコミットされた後です。

AutoLogアーカイブ・オプションを使用する際は、宛先属性をARCHまたはLGWR ASYNCに指定します。ARCHモードでは、ソース・データベースでのログ・スイッチの発生後に、REDO転送サービスによって、アーカイブREDOログ・ファイルがステージング・データベースにコピーされます。LGWRモードでは、REDOデータは、REDO転送サービスによってステージング・データベースにコピーされ、同時にソース・データベース上でオンラインREDOログ・ファイルに書き込まれます。

ログ・ファイルがアーカイブされるためには、非同期チェンジ・データ・キャプチャのソース・データベースは、ARCHIVELOGモードで実行されている必要があります。これは次のSQL文で指定します。

ALTER DATABASE ARCHIVELOG;

ARCHIVELOGモードでのデータベースの運用については、『Oracle Database管理者ガイド』を参照してください。

チェンジ・データ・キャプチャで使用されるREDOログ・ファイルは、チェンジ・データ・キャプチャでキャプチャされるまで、ステージング・データベースで使用できる状態である必要があります。ただし、REDOログ・ファイルは、チェンジ・データ・キャプチャのサブスクライバが変更データを処理するまで使用できる状態である必要はありません。

特定のチェンジ・セットに対して、チェンジ・データ・キャプチャで必要なくなったREDOログ・ファイルを判別するには、パブリッシャは、チェンジ・セットのStreams取得プロセスを変更して、Streamsが内部クリーンアップを実行し、DBA_LOGMNR_PURGED_LOGビューを移入するようにします。パブリッシャは次の手順に従います。

1. ステージング・データベースで次の問合せを使用して、チェンジ・セットCHICAGO_DAILYに使用するfirst_scnの新しい適切な値の判別に必要な3つのSCN値を取得します。

SELECT cap.CAPTURE_NAME, cap.FIRST_SCN, cap.APPLIED_SCN,
cap.REQUIRED_CHECKPOINT_SCN
FROM DBA_CAPTURE cap, ALL_CHANGE_SETS cset
WHERE cset.SET_NAME = 'CHICAGO_DAILY' AND
cap.CAPTURE_NAME = cset.CAPTURE_NAME;

CAPTURE_NAME                    FIRST_SCN APPLIED_SCN REQUIRED_CHECKPOINT_SCN
------------------------------ ---------- ----------- -----------------------
CDC$C_CHICAGO_DAILY                778059      778293                  778293

2. 元のfirst_scn値以上で、手順1の問合せで戻されたapplied_scn値およびrequired_checkpoint_scn値以下の、新しいfirst_scn値を決定します。例では、この値は778293で、取得プロセス名はCDC$C_CHICAGO_DAILYなので、パブリッシャは、取得プロセスのfirst_scn値を次のように変更できます。

BEGIN
DBMS_CAPTURE_ADM.ALTER_CAPTURE(
capture_name => 'CDC$C_CHICAGO_DAILY',
first_scn    => 778293);
END;
/

これらの基準を満たすSCN値がない場合、チェンジ・セットでは、そのREDOログ・ファイルすべてが必要です。

3. DBA_LOGMNR_PURGEDLOGビューを問い合せて、チェンジ・データ・キャプチャで必要なくなったログ・ファイルをすべて参照します。

SELECT FILE_NAME
FROM DBA_LOGMNR_PURGED_LOG;

注意:
REDOログ・ファイルは、チェンジ・データ・キャプチャ以外の目的で、ステージング・データベースで必要となる場合があります。REDOログ・ファイルを削除する前に、パブリッシャは、他のユーザーが必要としていないことを確認する必要があります。

既存の取得プロセスのfirst_scnの設定および取得プロセスのチェックポイントの詳細は、『Oracle Streams概要および管理』を参照してください。

AutoLogチェンジ・ソースのすべてのチェンジ・セットについて、first_scn値は、DBMS_CDC_PUBLISH.ALTER_AUTOLOG_CHANGE_SOURCE first_scnパラメータを使用して更新できます。AutoLogチェンジ・ソースのすべてのチェンジ・セットについて、新しいfirst_scn値は、前述の手順2で示した基準を満たす必要があることに注意してください。

REDOログ・ファイルのサイズおよびログ・スイッチが発生する頻度のどちらも、ソース・データベースでのアーカイブ・ログ・ファイルの生成に影響を及ぼす可能性があります。チェンジ・データ・キャプチャについて、REDOログ・ファイルをどのサイズにするかを決定する最も重要な要因は、変更が行われた時点から、その変更データがサブスクライバで使用可能になるまでの待機時間の許容範囲です。ただし、Oracle Databaseソフトウェアは、各ログ・スイッチでチェックポイント取得を試みるので、REDOログ・ファイルが小さすぎると、頻繁なログ・スイッチによって、チェックポイントの取得も頻繁になり、ソース・データベースのパフォーマンスにマイナスの影響を与えることになります。

ログ・ファイルのアーカイブ情報を監視する手順は、『Oracle Data Guard概要および管理』を参照してください。ソース・データベースとステージング・データベースという用語は、Oracle Data Guardの、プライマリ・データベースとアーカイブ先という用語にそれぞれ置き換えてください。

REDO転送サービスを使用して、AutoLogチェンジ・ソースにREDOログ・ファイルを供給する際に、REDOログ・ファイルの順序のギャップは、自動的に検出および解決されます。AutoLogチェンジ・セットにログ・ファイルを手動で追加する必要がある状況が発生した場合、パブリッシャは、ログ・ファイルをダウンストリーム取得プロセスに明示的に割り当てる手順を使用できます(『Oracle Streams概要および管理』を参照)。これらの手順では、AutoLogチェンジ・セットの取得プロセスの名前が必要です。パブリッシャは、ALL_CHANGE_SETSデータ・ディクショナリ・ビューで、AutoLogチェンジ・セットの取得プロセスの名前を確認できます。

非同期チェンジ・データ・キャプチャとサプリメンタル・ロギング

チェンジ・データ・キャプチャの非同期モードは、ソース・データベースでのサプリメンタル・ロギングが適切な場合に最も機能します。サプリメンタル・ロギングは、同期チェンジ・データ・キャプチャでは使用されません。

ソース・データベースDBAは、データベース・レベルのなんらかの形式のサプリメンタル・ロギングを有効化する必要があります。次の例では、データベース・レベルの最小サプリメンタル・ロギングを有効化します。

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

また、ソース・データベースDBAに関する推奨事項は次のとおりです。

- ファンクションまたは主キーの一部であるすべてのソース・テーブル列をサプリメンタル・ロギングして、行を一意に識別します。これは、データベース・レベルまたは表レベルの識別キー・ロギングを使用するか、表レベルの無条件のログ・グループを介して行うことができます。

- 任意の非同期チェンジ・テーブルによってキャプチャされるソース・テーブル列すべてに対して、無条件のログ・グループを作成します。これは、ソース・テーブルにチェンジ・テーブルが作成される前に行う必要があります。

ALTER TABLE sh.promotions
ADD SUPPLEMENTAL LOG GROUP log_group_cust
(PROMO_NAME, PROMO_SUBCATEGORY, PROMO_CATEGORY) ALWAYS;

キャプチャされるすべてのソース・テーブル列に対して無条件のログ・グループを作成しない場合、更新DML操作が発生する際に、チェンジ・テーブル内の変更されていないユーザー列値の一部が、実際のソース・テーブル値を反映せずにNULLとなります。

たとえば、ソース・テーブルに、XとYという2つの列が格納されており、ソース・データベースDBAは、そのテーブルに対してY列のみが含まれる無条件のログ・グループを定義しているものと仮定します。さらに、ユーザーはその表の行のY列のみ更新するものとします。サブスクライバがその行の変更データを参照する際、変更されていない列Xの値はNULLとなります。ただし、Xの実際の列値はREDOログ・ファイルから除外されており、チェンジ・テーブルに含めることができないので、サブスクライバは、X列の実際のソース・テーブル値がNULLであることが推測できません。サブスクライバは、列Xの実際のソース・テーブル値がNULLであるか、変更されていないのかを判別するには、TARGET_COLMAP$制御列の内容で判断します。

様々なタイプのサプリメンタル・ロギングの詳細は、『Oracle Databaseユーティリティ』を参照してください。

非同期チェンジ・データ・キャプチャとOracle Streamsコンポーネント

非同期チェンジ・データ・キャプチャでは、変更データのキャプチャおよびチェンジ・セットの移入を行うために、Oracle Streamsのコンポーネント(構成要素)が生成されます。これらのコンポーネントを、他の目的で再利用または共有しないでください。たとえば、分散HotLogチェンジ・ソースに対してチェンジ・データ・キャプチャによって生成されるキャプチャ・キューは、ユーザーが新たに作成したStreams伝播用のソース・キューとしては使用できません。

HotLogおよびAutoLogモードの場合、各チェンジ・セットには、Streamsの取得プロセス、キュー、キュー表および適用プロセスが含まれます。これらのオブジェクトはすべてステージング・データベースのパブリッシャが所有します。分散HotLogモードの場合、各チェンジ・ソースはソース・データベース上に存在し、Streamsの取得プロセス、キューおよびキュー表が含まれます。各チェンジ・セットはステージング・データベース上に存在し、それぞれにStreamsの適用プロセスが含まれます。チェンジ・ソースに最初のチェンジ・セットを作成すると、チェンジ・ソース内のすべてのチェンジ・セット間で共有されるキューおよびキュー表がチェンジ・データ・キャプチャによってステージング・データベース上に生成されます。また、チェンジ・ソース・キューからステージング・データベースのチェンジ・セット・キューまでソース・データベースのStreamsの伝播が生成されます。ソース・データベース・オブジェクトはソース・データベースのパブリッシャが所有し、ステージング・データベース・オブジェクトはステージング・データベースのパブリッシャが所有します。

図17-8は、非同期分散HotLogチェンジ・データ・キャプチャ環境でStreamsコンポーネントがどのように使用されるかを例示しています。この例では、ソース・データベース上に1つの分散HotLogチェンジ・ソースdisthot_change_srcがあり、ステージング・データベース上に2つのチェンジ・セットChange_Set_1およびChange_Set_2があります。

図17-8 非同期分散HotLogチェンジ・データ・キャプチャ・システムのStreamsコンポーネント

非同期チェンジ・データ・キャプチャでサポートされるデータ型と表構造

非同期チェンジ・データ・キャプチャでは、次のものを除く、Oracleのすべての組込みデータ型の列をサポートしています。

- BFILE

- LONG

- ROWID

- UROWID

- オブジェクト・タイプ(XMLTypeなど)

仮想列はサポートされません。さらに、チェンジ・データ・キャプチャでは、結果として引用符付き識別子の使用はサポートされず、チェンジ・データ・キャプチャの予約キーワードの使用もサポートされません。予約キーワードのリストについては、『Oracle Database SQL言語リファレンス』を参照してください。

非同期チェンジ・データ・キャプチャでは、次の表構造をサポートしません。

- 一時表であるソース・テーブル

- オブジェクト表であるソース・テーブル

- サポートされていないデータ型の列(LOB列を含む)またはオーバーフロー・セグメントを持つ索引構成表

NOLOGGINGおよびUNRECOVERABLE操作の制限

SQL操作のNOLOGGINGまたはUNRECOVERABLEキーワードを使用する場合、非同期チェンジ・データ・キャプチャでは、その操作からの変更をキャプチャできません。同様に、SQL*Loaderのダイレクト・パス・ロードで制御ファイルにUNRECOVERABLE句を使用すると、ダイレクト・ロードからの変更は、非同期チェンジ・データ・キャプチャではキャプチャできません。

NOLOGGINGおよびUNRECOVERABLEキーワードについては『Oracle Database SQL言語リファレンス』を、ダイレクト・パス・ロードおよびSQL*Loaderについては『Oracle Databaseユーティリティ』を参照してください。

実装とシステム構成

チェンジ・データ・キャプチャは、適切なOracleドライバがすでにインストールされた状態でパッケージ化されており、非同期または同期のデータ・キャプチャを実装できます。チェンジ・データ・キャプチャの同期モードはStandard Editionで利用できますが、非同期モードを使用する場合はEnterprise Editionが必要です。

また、チェンジ・データ・キャプチャではJavaが使用されることに注意してください。したがって、Oracle Databaseのインストール時に、Javaが使用可能になっているかどうかを確認してください。

チェンジ・データ・キャプチャでは、CREATE TABLE、ALTER TABLEおよびDROP TABLEの各SQL文に対してシステム・トリガーが配置されます。システム・トリガーがソース・データベースに対して使用禁止にされていると、チェンジ・データ・キャプチャは正しく動作しません。したがって、システム・トリガーは使用禁止にしないでください。

チェンジ・データ・キャプチャをデータベースから削除するために、adminディレクトリにSQLスクリプトrmcdc.sqlが用意されています。これは、チェンジ・データ・キャプチャによって、CREATE TABLE、ALTER TABLEおよびDROP TABLEの各SQL文に配置されたシステム・トリガーを削除します。さらに、rmcdc.sqlは、チェンジ・データ・キャプチャで使用されるJavaクラスをすべて削除します。rmcdc.sqlをコールした後は、システムでチェンジ・データ・キャプチャが動作しなくなることに注意してください。システム管理者がデータベースからJava仮想マシンを削除することにした場合、rmjvmをコールする前にrmcdc.sqlをコールする必要があります。

チェンジ・データ・キャプチャを再インストールするために、adminディレクトリにSQLスクリプトinitcdc.sqlが用意されています。これは、チェンジ・データ・キャプチャに必要なチェンジ・データ・キャプチャのシステム・トリガーとJavaクラスを作成します。チェンジ・データ・キャプチャを再インストールするには、Java仮想マシンを使用できるようにする必要があります。

Database Configuration Assistantの考慮事項

Database Configuration Assistantで提供される事前定義のテンプレートはすべて、Oracleのチェンジ・データ・キャプチャ機能をサポートしています。事前定義のテンプレートには次のものがあります。

- 汎用

- データ・ウェアハウス

- トランザクション処理

- 新規データベース

「新規データベース」オプションを選択してカスタム・データベースを作成する場合、「追加」データベース構成ダイアログ・ボックスではデフォルトで「Oracle JVM」が選択されます。Oracleのチェンジ・データ・キャプチャではOracle JVM機能が必要なので、この設定を変更しないてください。

サポートされる分散HotLog構成および制限の概要

以降の項では、チェンジ・データ・キャプチャの分散HotLogモードでサポートされている構成および制限について説明します。

ソース・データベースおよびステージング・データベースのOracle Databaseのリリース

チェンジ・データ・キャプチャの分散HotLogモードでは、ソース・データベースおよびステージング・データベースに次のOracle Databaseのリリースを組み合せて使用できます。

- Oracle Databaseリリース9.2(9.2.0.6以上のパッチ・セット)、10.1、10.2、11.1または11.2のソース・データベース。

- Oracle Databaseリリース11.1または11.2のステージング・データベース。

注意:
リリース9.2または10.1のOracle Databaseがチェンジ・データ・キャプチャの分散HotLogモードのソース・データベースとなる場合、ソース・データベースのメタデータはステージング・データベース上に格納されるのに対して、Oracle Databaseリリース11.1または11.2のソース・データベースのメタデータはソース・データベース上に格納されます。したがって、Oracle Databaseリリース9.2またはリリース10.1のソース・データベースのメタデータを参照するには、パブリッシャは、ステージング・データベースに対してCHANGE_SOURCESデータ・ディクショナリ・ビューを問い合せ、Oracle Databaseリリース11.1または11.2のソース・データベースのメタデータを参照するには、ソース・データベースに対してCHANGE_SOURCESデータ・ディクショナリ・ビューを問い合せる必要があります。

分散HotLogチェンジ・ソースのリリース11(11.1または11.2)へのアップグレード

前述のとおり、Oracle Databaseリリース11の分散HotLogチェンジ・ソースのメタデータはソース・データベースに格納されますが、リリース9.2または10.1のチェンジ・ソースのメタデータはステージング・データベースに格納されます。

リリース10.2のOracle Databaseをリリース11にアップグレードするときは、メタデータを移動する必要があります。

リリース9.2または10.1のOracle Databaseをリリース11にアップグレードする際、そのアップグレード操作の一部として、チェンジ・データ・キャプチャによってステージング・データベースからソース・データベースにソース・データベースのメタデータが移動されることはありません。ただし、アップグレード後に最初にチェンジ・ソースを有効化(DBMS_CDC_PUBLISH.ALTER_HOTLOG_CHANGE_SOURCEプロシージャを使用)すると、メタデータをアップグレードしてソース・データベースに移動する必要があることがチェンジ・データ・キャプチャによって検出され、これらの操作が自動的に実行されます。

パブリッシャがアップグレードの直後にチェンジ・ソースを変更しないことを選択する場合、より適切なタイミングが訪れるまで、チェンジ・ソース・メタデータをステージング・データベース上に留めておくことができます。ただし、Oracle Databaseリリース11にアップグレードした後は、ソース・データベース・メタデータを無制限にステージング・データベース上に残しておかないことをお薦めします。これは、分散HotLogチェンジ・ソースに関する情報を、ソース・データベースのデータ・ディクショナリ・ビューで利用できるようにするためです。

ハードウェア・プラットフォームおよびオペレーティング・システム

チェンジ・データ・キャプチャの分散HotLogモードでは、ソース・データベースおよびステージング・データベースに異なるハードウェア・プラットフォームやオペレーティング・システム(またはその両方)を使用できます。

ステージング・データベースにおける複数のパブリッシャの要件

チェンジ・データ・キャプチャの分散HotLogモードのステージング・データベースにパブリッシャが複数存在し、1人のパブリッシャが別のパブリッシャの分散HotLogチェンジ・セットのチェンジ・テーブルを定義している場合、チェンジ・データ・キャプチャは、そのチェンジ・セットを作成したパブリッシャが確立したデータベース・リンクを使用して、ソース・データベースにアクセスします。したがって、チェンジ・セットを作成したパブリッシャが確立した、ソース・データベースへのデータベース・リンクは、チェンジ・テーブルを正常に作成するために失われてはならないものです。チェンジ・テーブル作成時にチェンジ・セットのパブリッシャのデータベース・リンクが存在しないと、リモート・データベースの接続が見つからなかったことを示すエラーが戻されます。

データベース・リンクの要件

ソース・データベースからステージング・データベースへのデータベース・リンクは、分散HotLogチェンジ・ソースの存在期間中は存在している必要があります。

ステージング・データベースからソース・データベースへのデータベース・リンクは、分散HotLogのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルの作成、変更または削除時には存在している必要があります。ただし、このデータベース・リンクは、変更のキャプチャのために必要ではありません。必要な分散HotLogのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルを配置および有効化すると、変更のキャプチャを妨げずにこのデータベース・リンクを削除できます。データベース・リンクを削除した場合、分散HotLogのチェンジ・ソース、チェンジ・セットおよびチェンジ・テーブルを作成、変更または削除する際に再作成する必要があります。

第V部

データ・ウェアハウスのパフォーマンス

この部では、データ・ウェアハウスのパフォーマンスを向上させる方法について説明します。次の章で構成されています。

- 第18章「基本的なクエリー・リライト」

- 第19章「高度なクエリー・リライト」

- 第20章「スキーマのモデリング化技法」

- 第21章「データ・ウェアハウスにおける集計のためのSQL」

- 第22章「分析計算およびレポート用SQL関数」

- 第23章「モデリングのSQL」

- 第24章「高度なビジネス・インテリジェンス問合せ」

18 基本的なクエリー・リライト

この章では、Oracleのクエリー・リライトについて説明します。次の内容が含まれます。

- クエリー・リライトの概要

- クエリー・リライトの有効化

- クエリー・リライトの例

クエリー・リライトの概要

実表にデータが大量に格納されていると、必要な集計やこれらの表間の結合の計算にコストや時間が多くかかります。こうした場合の問合せは数分から数時間を要することもあります。マテリアライズド・ビューにはすでに計算された集計と結合が含まれているため、Oracle Databaseでは、クエリー・リライトと呼ばれる非常に強力なプロセスが採用されており、マテリアライズド・ビューを使用した問合せに迅速に応答します。

クエリー・リライトが利用可能になることは、マテリアライズド・ビューを作成しメンテナンスすることで得られる主要なメリットの1つです。クエリー・リライトでは、複数の表やビューに対するSQL文を、ディテール表に定義された1つ以上のマテリアライズド・ビューに対してアクセスする文に変換します。この変換はエンド・ユーザーやアプリケーションに対して透過的に処理され、SQL文内のマテリアライズド・ビューに対する介入や参照は不要です。クエリー・リライトは透過的な処理なので、マテリアライズド・ビューは、アプリケーション・コード内のSQLを無効にせず索引のように追加したり削除したりできます。

問合せは、その問合せにクエリー・リライトが必要かどうかを判断するチェックを受けます。チェック結果に問題があると、その問合せはマテリアライズド・ビューではなく、ディテール表に対する問合せになります。この形式は、応答時間や処理能力の点で効率的でない場合があります。

オプティマイザでは、マテリアライズド・ビューが関わる問合せをリライトする場合の判断に2つの方法が使用されます。はじめに、問合せのSQLテキストとマテリアライズド・ビュー定義のSQLテキストとの一致により判断する方法が使用されます。この方法で判断できない場合、問合せとマテリアライズド・ビューの結合、選択、データ列、グルーピング列および集計関数を比較するという、より一般的な方法が取られます。

クエリー・リライトは、次のSQL文の問合せおよび副問合せに対応します。

- SELECT

- CREATE TABLE … AS SELECT

- INSERT INTO … SELECT

クエリー・リライトは、集合演算子UNION、UNION ALL、INTERSECT、MINUS、およびDML文の副問合せ(INSERT、DELETE、UPDATEなど)にも対応します。

特定の問合せをリライトして、1つ以上のマテリアライズド・ビューを使用するようにするかどうかには、ディメンション、制約、およびリライトの整合性レベルが影響します。また、問合せのリライトはREWRITEヒントやNOREWRITEヒント、およびQUERY_REWRITE_ENABLEDセッション・パラメータを使用して有効化したり無効化したりできます。

問合せでクエリー・リライトが可能かどうか、また可能な場合はどのマテリアライズド・ビューが使用されるかについては、DBMS_MVIEW.EXPLAIN_REWRITEプロシージャで示されます。また、このプロシージャでは問合せをリライトできない理由もわかります。

Oracleによるクエリー・リライト条件

問合せは、次の一定の条件が満たされた場合にのみリライトされます。

- セッションで、クエリー・リライトが使用可能である必要があります。

- マテリアライズド・ビューに対するクエリー・リライトが使用可能である必要があります。

- リライトの整合性レベルが、マテリアライズド・ビューの使用を許可している必要があります。たとえば、マテリアライズド・ビューが最新のものではなく、かつクエリー・リライトの整合性がENFORCEDに設定されている場合、マテリアライズド・ビューは使用できません。

- 問合せの要求結果の一部またはすべてが、1つ以上のマテリアライズド・ビューに格納されている、事前計算された結果から取得可能である必要があります。

オプティマイザでは、こうした条件のテストに、ユーザーが制約やディメンションを使用して宣言したデータ関係が使用される場合があります。そうしたデータ関係には、階層、参照整合性、キー・データの一意性などがあります。

クエリー・リライトの有効化

クエリー・リライトを使用可能にするには、次の手順を実行する必要があります。

1. 個々のマテリアライズド・ビューに、ENABLE QUERY REWRITE句を指定します。

2. セッション・パラメータQUERY_REWRITE_ENABLEDをTRUE(デフォルト)またはFORCEに設定します。

3. 初期化パラメータOPTIMIZER_MODEを、ALL_ROWS、FIRST_ROWSまたはFIRST_ROWS_nに設定して、コストベース・オプティマイザを使用します。

手順1が実行されていないと、マテリアライズド・ビューはクエリー・リライトに使用できません。ENABLE QUERY REWRITEは、ALTER MATERIALIZED VIEW文を使用して指定するか、マテリアライズド・ビューの作成時に次のように指定できます。

CREATE MATERIALIZED VIEW join_sales_time_product_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_id, p.prod_name, t.time_id, t.week_ending_day,
s.channel_id, s.promo_id, s.cust_id, s.amount_sold
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id  AND s.prod_id = p.prod_id;

NOREWRITEヒントを使用すると、QUERY_REWRITE_ENABLEDパラメータをオーバーライドして、SQL文のクエリー・リライトを使用禁止にできます。REWRITEヒント(mv_nameと併用)を使用すると、クエリー・リライトを使用できるマテリアライズド・ビューを、ヒントで指定するビューのみに制限できます。

DBMS_ADVISOR.TUNE_MVIEWプロシージャを使用すると、CREATE MATERIALIZED VIEW文を最適化して、一般的なQUERY REWRITEを使用可能にできます。

クエリー・リライトの初期化パラメータ

クエリー・リライトの動作は、次の3種類の初期化パラメータの設定によって制御します。

- OPTIMIZER_MODE = ALL_ROWS(デフォルト)、FIRST_ROWSまたはFIRST_ROWS_n
OPTIMIZER_MODEをFIRST_ROWSに設定すると、コストと発見的方法の組合せによって、最初の数行を高速に配信するために最適な計画が求められます。FIRST_ROWS_nに設定すると、コストベースのアプローチが使用され、初めのn行(n = 1、10、100、1000)を戻すまでの時間が最も速くなるように最適化されます。

- QUERY_REWRITE_ENABLED = TRUE(デフォルト)、FALSEまたはFORCE
このオプションを使用すると、オプティマイザのクエリー・リライト機能が有効化されてマテリアライズド・ビューを利用できるようになり、パフォーマンスが向上します。FALSEに設定すると、オプティマイザのクエリー・リライト機能が無効になり、リライトされていない問合せの見積り問合せコストの方が低くなる場合にも、マテリアライズド・ビューを使用した問合せのリライトが行われません。

FORCEに設定すると、オプティマイザのクエリー・リライト機能が有効になり、リライトされない問合せの見積り問合せコストの方が低くなる場合にも、マテリアライズド・ビューを使用して問合せがリライトされます。

- QUERY_REWRITE_INTEGRITY
このパラメータはオプションですが、設定する場合は、STALE_TOLERATED、TRUSTEDまたはENFORCED(デフォルト)に設定する必要があります(「クエリー・リライトの精度」を参照)。

デフォルトでは、整合性レベルはENFORCEDに設定されます。このモードでは、すべての制約の妥当性チェックを行う必要があります。そのため、ENABLE NOVALIDATE RELYを使用した場合、一部のクエリー・リライトが動作しないことがあります。この環境でクエリー・リライトを使用可能にする(つまり、制約の妥当性チェックが行われないようにする)には、整合性レベルをTRUSTEDやSTALE_TOLERATEDのような低いレベルに設定する必要があります。

クエリー・リライトの制御

マテリアライズド・ビューをクエリー・リライトに使用できるのは、ENABLE QUERY REWRITE句が指定されている場合のみです。この句はマテリアライズド・ビューを最初に作成するときに指定するか、後でALTER MATERIALIZED VIEW文を使用して指定します。

前述のセッション・パラメータは、すべてのセッションについて、ALTER SYSTEM SET文を使用して設定でき、また初期化ファイルでも設定できます。所定のユーザー・セッションでALTER SESSIONを使用すると、そのセッションのみでクエリー・リライトの使用を禁止または可能にできます。次に例を示します。

ALTER SESSION SET QUERY_REWRITE_ENABLED = TRUE;

クエリー・リライトのレベルはセッションごとに設定できるので、各ユーザーが異なる整合性レベルで作業できます。次のような文を使用できます。

ALTER SESSION SET QUERY_REWRITE_INTEGRITY = STALE_TOLERATED;
ALTER SESSION SET QUERY_REWRITE_INTEGRITY = TRUSTED;
ALTER SESSION SET QUERY_REWRITE_INTEGRITY = ENFORCED;

クエリー・リライトの精度

クエリー・リライトには、セッション・パラメータQUERY_REWRITE_INTEGRITYによって制御されるリライト整合性レベルが3つ用意されています。この整合性レベルは、パラメータ・ファイルに設定するか、ALTER SYSTEM文またはALTER SESSION文を使用して制御できます。この3つの値は次のとおりです。

- ENFORCED
これはデフォルトのモードです。オプティマイザでは、マテリアライズド・ビューの最新データのみが使用され、ENABLED VALIDATEDになっている主/一意/外部キー制約に基づいた関係のみが使用されます。

- TRUSTED
TRUSTEDモードの場合、オプティマイザでは、ディメンションで宣言された関係およびRELY制約が適切であることが信頼の対象になります。このモードでは、事前作成マテリアライズド・ビューや、ビューに基づくマテリアライズド・ビューが使用され、施行された関係と同様に施行されていない関係も使用されます。また、宣言されたがENABLED VALIDATEDでない主/一意キー制約、およびディメンションを使用して指定されたデータ関係も信頼されます。このモードではより高度なクエリー・リライト機能を使用できますが、ユーザーが宣言し、信頼された関係に不正確なものがあった場合、不正確な結果が生成される危険性もあります。

- STALE_TOLERATED
STALE_TOLERATEDモードの場合、オプティマイザでは最新データを含むマテリアライズド・ビューの他に、有効だが失効データを含むマテリアライズド・ビューも使用されます。このモードでは、リライト機能を最大限に使用できますが、不正確な結果が生成される危険性もあります。

リライト整合性が最も安全なレベルであるENFORCEDに設定されている場合、オプティマイザでは、問合せの結果がディテール表に直接アクセスした場合と同じであることを保証するために、施行された主キー制約および参照整合性制約のみが使用されます。リライト整合性をENFORCED以外のレベルに設定すると、次のような状況において、リライトした場合とリライトしなかった場合の出力が異なることがあります。

- マテリアライズド・ビューが、データのマスター・コピーと同期されていない場合。これは、通常、マテリアライズド・ビューの1つ以上のディテール表に対するバルク・ロードまたはDML操作の後に、マテリアライズド・ビューのリフレッシュ・プロシージャが保留状態にあるために発生します。データ・ウェアハウス・サイトによっては、この状況が最適な場合もあります。これは、一部のマテリアライズド・ビューでは一定の間隔でリフレッシュされることが一般的であるためです。

- ディメンション・オブジェクトに含まれる関係が無効の場合。たとえば、階層内のあるレベルの値が、正確に1つの親の値にロールアップされないことがあります。

- 事前作成マテリアライズド・ビュー表に格納された値が不適切な場合。

- 施行されていない表またはビューの制約により不正なデータ関係が定義されているため、間違った答えが生じている場合。

クエリー・リライトの有効化の権限

マテリアライズド・ビューの使用は、そのマテリアライズド・ビューに対してユーザーが保持する権限ではなく、問合せ内のディテール表またはビューに対してユーザーが保持する権限に基づきます。

GRANT QUERY REWRITEシステム権限では、自スキーマ内のマテリアライズド・ビューから直接参照される表がすべて自スキーマ内にある場合にのみ、そのマテリアライズド・ビューに対するクエリー・リライトが有効になります。GRANT GLOBAL QUERY REWRITE権限では、マテリアライズド・ビューが別のスキーマ内のオブジェクトを参照する場合にも、マテリアライズド・ビューに対するクエリー・リライトが有効になります。また、自スキーマの外部にある表およびビューの場合、QUERY REWRITEオブジェクト権限を使用することもできます。

クエリー・リライト用にマテリアライズド・ビューを使用するための権限は、定義者権限のプロシージャに対する権限と似ています。

サンプル・スキーマおよびマテリアライズド・ビュー

次の項では、shサンプル・スキーマおよびいくつかのマテリアライズド・ビューを使用して、オプティマイザでデータ関係を利用してクエリー・リライトが行われる仕組みを説明します。

この章におけるクエリー・リライトの例では、主として次に示すマテリアライズド・ビューを参照します。これらのマテリアライズド・ビューは、必ずしもshスキーマの最も効率的な実装を表しているわけではありません。リライト機能を表すためのベースにすぎません。この章には、特定の機能についての例も記載されています。

次に示すのは、結合および集計を含むマテリアライズド・ビューです。

CREATE MATERIALIZED VIEW sum_sales_pscat_week_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_subcategory, t.week_ending_day,
SUM(s.amount_sold) AS sum_amount_sold
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id  AND s.prod_id=p.prod_id
GROUP BY p.prod_subcategory, t.week_ending_day;

CREATE MATERIALIZED VIEW sum_sales_prod_week_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_id, t.week_ending_day, s.cust_id,
SUM(s.amount_sold) AS sum_amount_sold
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id AND s.prod_id=p.prod_id
GROUP BY p.prod_id, t.week_ending_day, s.cust_id;

CREATE MATERIALIZED VIEW sum_sales_pscat_month_city_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_subcategory, t.calendar_month_desc, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold,
COUNT(s.amount_sold) AS count_amount_sold
FROM   sales s, products p, times t, customers c
WHERE  s.time_id=t.time_id AND s.prod_id=p.prod_id AND s.cust_id=c.cust_id
GROUP BY p.prod_subcategory, t.calendar_month_desc, c.cust_city;

次に示すのは、結合のみを含むマテリアライズド・ビューです。

CREATE MATERIALIZED VIEW join_sales_time_product_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_id, p.prod_name, t.time_id, t.week_ending_day,
s.channel_id, s.promo_id, s.cust_id, s.amount_sold
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id AND s.prod_id = p.prod_id;

CREATE MATERIALIZED VIEW join_sales_time_product_oj_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_id, p.prod_name, t.time_id, t.week_ending_day,
s.channel_id, s.promo_id, s.cust_id, s.amount_sold
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id AND s.prod_id=p.prod_id(+);

これは必須ではありませんが、できるだけマテリアライズド・ビューに関する統計情報を収集して、問合せをリライトするかどうかをオプティマイザで判断できるようにすることをお薦めします。収集する場合、オブジェクトごとに収集するか、統計情報のない新規に作成したオブジェクトすべてを対象として収集するかを選択できます。オブジェクトごとの場合を次のjoin_sales_time_product_mvの例で示します。

EXECUTE DBMS_STATS.GATHER_TABLE_STATS ( -
'SH','JOIN_SALES_TIME_PRODUCT_MV', estimate_percent   => 20, -
block_sample   => TRUE, cascade   => TRUE);

次に、統計情報のない新規作成したオブジェクトすべてを収集対象にする場合の例を示します。

EXECUTE DBMS_STATS.GATHER_SCHEMA_STATS ( 'SH', -
options            => 'GATHER EMPTY', -
estimate_percent   =>  20, block_sample  =>  TRUE, -
cascade            =>  TRUE);

クエリー・リライトの発生を確認する方法

クエリー・リライトは透過的に行われるので、問合せがリライトされたかどうかを確認するには、特別なステップを実行する必要があります。問合せが高速に実行された場合、リライトが発生したと考えられますが、これは確認にはなりません。そのため、EXPLAIN PLAN文またはDBMS_MVIEW.EXPLAIN_REWRITEプロシージャを使用して、クエリー・リライトが発生したことを確認します。詳細は、「クエリー・リライトが発生したことの確認」を参照してください。

クエリー・リライトの例

次のcal_month_sales_mvというマテリアライズド・ビューの場合を考えてみます。このマテリアライズド・ビューを使用すると、月ごとの販売額(ドル)の合計を表示できます。

CREATE MATERIALIZED VIEW cal_month_sales_mv
ENABLE QUERY REWRITE AS
SELECT t.calendar_month_desc, SUM(s.amount_sold) AS dollars
FROM sales s, times t WHERE s.time_id = t.time_id
GROUP BY t.calendar_month_desc;

たとえば、その店舗における販売数量が通常の月では100万前後だとします。この場合、このマテリアライズド集計ビューには、事前に計算された月ごとの合計販売額(ドル)が用意されます。ここで、次の問合せについて考えてみます。この問合せは、その店舗における会計月ごとの総販売数量を問い合せるためのものです。

SELECT t.calendar_month_desc, SUM(s.amount_sold)
FROM sales s, times t WHERE s.time_id = t.time_id
GROUP BY t.calendar_month_desc;

前述のマテリアライズド・ビューおよびクエリー・リライト機能がない場合、sales表が直接アクセスされ、総販売数量が計算された上でその結果が返されることになります。その場合、sales表から膨大な数の行が読み込まれ、対象となるディスクへのアクセスに伴って問合せの応答時間は必ず増大します。また、問合せに結合があるので、膨大な数の行に対する結合の計算が必要になり、問合せへの応答はさらに遅くなります。ここで、マテリアライズド・ビューcal_month_sales_mvがあると、クエリー・リライトによって前述の問合せが透過的にリライトされ、次の問合せに書き換えられます。

SELECT calendar_month, dollars
FROM cal_month_sales_mv;

マテリアライズド・ビューcal_month_sales_mvの行数はほんの数十行で、結合も存在しないため、Oracle Databaseにより結果は即座に戻されます。このような単純な例からも、マテリアライズド・ビューを使用したクエリー・リライトの威力は明らかです。

19 高度なクエリー・リライト

この章では、Oracleの高度なクエリー・リライトについて説明します。次の内容が含まれます。

- Oracleによる問合せのリライト方法

- クエリー・リライトのタイプ

- その他のクエリー・リライトの考慮事項

- 同等化を使用した高度なクエリー・リライト

- 同等化を使用した結果キャッシュ・マテリアライズド・ビューの作成

- クエリー・リライトが発生したことの確認

- クエリー・リライトを改善するための設計上の考慮事項

Oracleによる問合せのリライト方法

オプティマイザが問合せをリライトするには、いくつかの方法があります。クエリー・リライトが可能かどうかを判断する最初のステップは、問合せが次の前提条件を満たしているかどうかを確認することです。

- マテリアライズド・ビュー内の結合がSQLで使用されている。

- 問合せに回答するのに十分なデータがマテリアライズド・ビューまたは複数のビューにある。

次に、オプティマイザは問合せをリライトする方法を判断する必要があります。最も簡単な例は、マテリアライズド・ビューに格納されている結果が、問合せによって要求されているものと正確に一致する場合です。オプティマイザは、問合せのテキストとマテリアライズド・ビュー定義のテキストを比較して、このような判断を行います。このテキスト一致の方法は最も簡単ですが、このタイプのクエリー・リライトに使用できる問合せの数は多くありません。

テキスト比較によるテストで判断できなかった場合、オプティマイザは、結合、選択、グルーピング、集計およびフェッチされた列データに基づいて、一連の一般的なチェックを実行します。これは、問合せの様々な句(SELECT、FROM、WHERE、HAVINGまたはGROUP BY)をマテリアライズド・ビューのものと比較することによって行われます。

この項では、オプティマイザの詳細およびクエリー・リライトの次のタイプについて説明します。

- テキスト一致リライト

- 一般的なクエリー・リライト方法

コストベースの最適化

問合せがリライトされると、Oracleのコストベース・オプティマイザによって、リライトされた問合せのコストと元の問合せのコストが比較され、コストの低い方の実行計画が選択されます。

クエリー・リライトは、コストベース・オプティマイザとともに使用できます。Oracle Databaseは、リライトを使用または使用せずに入力問合せを最適化し、最も効率的な方法を選択します。オプティマイザは、1つ以上の問合せブロックを一度に1つずつリライトすることで、問合せをリライトします。

クエリー・リライトが複数のマテリアライズド・ビューの中から問合せブロックをリライトするものを選択できる場合、読み込まれるデータ量の最も少ないものが選択されます。リライト用のマテリアライズド・ビューが選択されると、オプティマイザはリライトされた問合せがさらに別のマテリアライズド・ビューでリライト可能かどうかをテストします。このプロセスは、リライトができなくなるまで繰り返されます。その後、リライトされた問合せは最適化され、元の問合せも最適化されます。オプティマイザでは、この2つの最適化したものが比較され、コストの低い方が選択されます。

最適化はコストを基準にするため、問合せに関係する表と、マテリアライズド・ビューを示す表の両方についての統計情報を収集することが重要です。表の行数などの統計情報は、リライトされた問合せのコスト計算に使用する基本的な尺度です。その作成にはDBMS_STATSパッケージを使用します。

インライン・ビューまたは名前付きビューを含む問合せも、クエリー・リライトの対象になります。問合せに名前付きビューが含まれている場合、マテリアライズド・ビューと問合せを一致させるために、ビュー名が使用されます。問合せにインライン・ビューが含まれている場合、インライン・ビューは、マテリアライズド・ビューと問合せを一致させる前に、マージされる場合があります。

図19-1に、リライト処理中に使用されるコストベースのアプローチを図示します。

図19-1 クエリー・リライト・プロセス

一般的なクエリー・リライト方法

オプティマイザでは、様々なクエリー・リライト方法の中から選択して、問合せに対する回答を行います。テキストの一致によるリライトが不可能な場合、一般的なクエリー・リライトと呼ばれるリライト方法が使用されます。この高度なテクニックを使用するメリットは、多数の異なる問合せに対する回答に1つ以上のマテリアライズド・ビューを使用できる点です。したがって、クエリー・リライトを発生させるために、問合せとマテリアライズド・ビューが常に一致している必要はありません。

一般的なクエリー・リライト方法を使用する場合、オプティマイザでは、主キー制約、外部キー制約、ディメンション・オブジェクトなど、依存可能なデータ関係を使用します。たとえば、主キーと外部キーの関係によって、外部キー表の各行が主キー表の1つ以下の行と結合することがオプティマイザに示されます。さらに、外部キーにNOT NULL制約がある場合は、外部キー表の各行が主キー表の1つの行と正確に結合することが示されます。ディメンション・オブジェクトは、日、月、年などの関係を表し、これは、データを日レベルから月レベルにロールアップするために使用できます。

データの結合、グルーピングまたは集計操作によって生成される結果の種類が示されるため、このデータ関係は特に重要です。そのため、このようなデータ関係がデータベースに存在する場合、大規模な問合せ集合のリライトを可能にするには、制約およびディメンションを宣言する必要があります。

制約およびディメンションが必要な場合

表19-1に、異なるタイプのクエリー・リライトにディメンションおよび制約が必要な場合を示します。クエリー・リライトのこれらのタイプについては、この章の中で説明しています。

表19-1 クエリー・リライトのディメンションおよび制約要件

クエリー・リライトのタイプ

ディメンション

主キー/外部キー/Not Null制約

SQLテキスト一致

必要なし

必要なし

後戻り結合

必要あり または

必要あり

集計可能性

必要なし

必要なし

集計ロールアップ

必要なし

必要なし

ディメンションを使用したロールアップ

必要あり

必要なし

データのフィルタリング

必要なし

必要なし

PCTリライト

必要なし

必要なし

複数のマテリアライズド・ビュー

必要なし

必要なし

クエリー・リライトで行われるチェック

クエリー・リライトが発生するには、データが様々なチェックをパスする必要があります。これらのチェックには、次のものがあります。

- 結合互換性チェック

- データ充足性チェック

- グルーピング互換性チェック

- 集計可能性チェック

結合互換性チェック

このチェックでは、問合せの結合がマテリアライズド・ビューの結合と比較されます。一般に、この比較によって、結合は次の3つに分類されます。

- 問合せおよびマテリアライズド・ビューの両方に発生する共通結合。この結合は、共通のサブグラフを形成します。

- 問合せのみで発生し、マテリアライズド・ビューでは発生しないデルタ結合。この結合は、問合せのデルタ・サブグラフを形成します。

- マテリアライズド・ビューのみで発生し、問合せでは発生しないデルタ結合。この結合は、マテリアライズド・ビューのデルタ・サブグラフを形成します。

図19-2に、これらの結合を示します。

図19-2 クエリー・リライトのサブグラフ

共通結合

両者間の共通結合の組は同型であるか、または問合せの結合がマテリアライズド・ビューの結合から導出可能である必要があります。たとえば、マテリアライズド・ビューが表Aの表Bとの外部結合を含み、かつ、問合せが表Aの表Bとの内部結合を含んでいる場合、内部結合の結果は、外部結合の結果からアンチ結合行をフィルタすることで導出できます。たとえば、次の問合せを考えてみます。

SELECT p.prod_name, t.week_ending_day, SUM(s.amount_sold)
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id AND s.prod_id = p.prod_id
AND mv.week_ending_day BETWEEN TO_DATE('01-AUG-1999', 'DD-MON-YYYY')
AND TO_DATE('10-AUG-1999', 'DD-MON-YYYY')
GROUP BY p.prod_name, mv.week_ending_day;

次に、この問合せとマテリアライズド・ビューjoin_sales_time_product_mvとの間の共通結合を示します。

s.time_id = t.time_id AND s.prod_id = p.prod_id

これらは正確に一致し、次のようにリライトできます。

SELECT p.prod_name, mv.week_ending_day, SUM(s.amount_sold)
FROM   join_sales_time_product_mv
WHERE  mv.week_ending_day BETWEEN TO_DATE('01-AUG-1999','DD-MON-YYYY')
AND   TO_DATE('10-AUG-1999','DD-MON-YYYY')
GROUP BY mv.prod_name, mv.week_ending_day;

問合せは、マテリアライズド・ビューjoin_sales_time_product_oj_mvを使用しても結果を得ることができます。この場合、問合せの内部結合がマテリアライズド・ビューの外部結合から導出可能です。リライトされた問合せでは、(ユーザーには透過的に)アンチ結合行がフィルタによって排除されます。リライトされた問合せの構造は次のとおりです。

SELECT mv.prod_name, mv.week_ending_day, SUM(mv.amount_sold)
FROM   join_sales_time_product_oj_mv mv
WHERE  mv.week_ending_day BETWEEN TO_DATE('01-AUG-1999','DD-MON-YYYY')
AND  TO_DATE('10-AUG-1999','DD-MON-YYYY') AND mv.prod_id IS NOT NULL
GROUP BY mv.prod_name, mv.week_ending_day;

一般に、結合のみを含むマテリアライズド・ビューに外部結合を使用する場合は、マテリアライズド・ビューの外部結合の右側に主キーまたはROWIDを指定する必要があります。たとえば、前述の例のjoin_sales_time_product_oj_mvでは、salesとproductsの両方に主キーがあります。

結合のみを含むマテリアライズド・ビューの別の例に、セミ結合リライトの例があります。つまり、単一表を持つEXISTSまたはIN副問合せが含まれる問合せです。次のような、売上が1,000ドルを超えた製品をレポートする問合せを考えてみます。

SELECT DISTINCT p.prod_name
FROM products p
WHERE EXISTS (SELECT p.prod_id, SUM(s.amount_sold) FROM sales s
WHERE p.prod_id=s.prod_id HAVING SUM(s.amount_sold) > 1000)
GROUP BY p.prod_id);

この問合せは、次のように表すこともできます。

SELECT DISTINCT p.prod_name
FROM products p WHERE p.prod_id IN (SELECT s.prod_id FROM sales s
WHERE s.amount_sold > 1000);

この問合せには、products表とsales表の間のセミ結合(s.prod_id = p.prod_id)が含まれています。

この問合せは、外部キー制約がアクティブの場合、join_sales_time_product_mvマテリアライズド・ビューを使用するか、主キーがアクティブの場合はjoin_sales_time_product_oj_mvマテリアライズド・ビューを使用してリライトできます。どちらのマテリアライズド・ビューも、s.prod_id=p.prod_idが含まれます。これは、問合せ内のセミ結合を導出する場合に使用します。この問合せは次のように、join_sales_time_product_mvを使用してリライトされます。

SELECT mv.prod_name
FROM (SELECT DISTINCT mv.prod_name FROM  join_sales_time_product_mv mv
WHERE mv.amount_sold > 1000);

マテリアライズド・ビューjoin_sales_time_product_mvがtime_idでパーティション化された場合、salesとproductsの間の元の結合が回避されたため、この問合せは元の問合せより効率的になる傾向があります。この問合せは次のように、join_sales_time_product_oj_mvを使用してリライトされる場合もあります。

SELECT mv.prod_name
FROM (SELECT DISTINCT mv.prod_name FROM join_sales_time_product_oj_mv mv
WHERE mv.amount_sold > 1000 AND mv.prod_id IS NOT NULL);

セミ結合を使用したリライトは、結合のみを含むマテリアライズド・ビューに制限されており、結合および集計を含むマテリアライズド・ビューの場合には使用できません。

問合せデルタ結合

問合せデルタ結合は、問合せでのみ使用される結合で、マテリアライズド・ビューでは使用されません。問合せに指定できるデルタ結合の数や種類に制限はなく、問合せがマテリアライズド・ビューを使用してリライトされた場合は、簡単に保持されます。保持された結合が正常に機能するには、マテリアライズド・ビューに結合キーが含まれている必要があります。リライトの際、マテリアライズド・ビューは問合せデルタ内の適切な表に結合されます。たとえば、次の問合せを考えてみます。

SELECT p.prod_name, t.week_ending_day, c.cust_city, SUM(s.amount_sold)
FROM   sales s, products p, times t, customers c
WHERE  s.time_id=t.time_id AND s.prod_id = p.prod_id
AND    s.cust_id = c.cust_id
GROUP BY p.prod_name, t.week_ending_day, c.cust_city;

マテリアライズド・ビューjoin_sales_time_product_mvを使用した場合、共通結合はs.time_id=t.time_idおよびs.prod_id=p.prod_idになります。問合せのデルタ結合はs.cust_id=c.cust_idです。リライトされたフォームは、次のようにjoin_sales_time_product_mvマテリアライズド・ビューをcustomers表に結合します。

SELECT mv.prod_name, mv.week_ending_day, c.cust_city, SUM(mv.amount_sold)
FROM   join_sales_time_product_mv mv, customers c
WHERE  mv.cust_id = c.cust_id
GROUP BY mv.prod_name, mv.week_ending_day, c.cust_city;

マテリアライズド・ビュー・デルタ結合

マテリアライズド・ビューのデルタ結合は、マテリアライズド・ビューのみで使用される結合で、問合せでは使用されません。マテリアライズド・ビュー内のすべてのデルタ結合は、共通結合の結果に関して可逆式である必要があります。可逆式結合は、共通結合の結果が制限されないことを保証します。可逆式結合では、表Aと表Bが結合された場合、表Aの行は表Bの行と常に一致し、どのデータも消失しません。このため、可逆式結合と呼ばれます。たとえば、外部キーを使用する各行は、外部キーにNULLが許可されない場合、主キーを使用した1つの行と一致します。そのため、可逆式結合を保証するには、適切な結合キーに外部キー制約、主キー制約およびNOT NULL制約を指定する必要があります。または、表Aと表Bの結合が外部結合の場合(Aが外部表の場合)、結合は表Aのすべての行を保持するため可逆式となります。

マテリアライズド・ビュー内のすべてのデルタ結合は、共通結合の結果に関して重複していない必要があります。非重複結合は、共通結合の結果が重複されないことを保証します。たとえば、非重複結合では、表Aと表Bが結合された場合、表Aの行は表Bの1つ以下の行と一致し、重複は発生しません。非重複結合を保証するには、主キー制約または一意キー制約を使用して、表Bのキーを一意の値に制約する必要があります。

salesとtimesを結合する次の問合せを考えてみます。

SELECT t.week_ending_day, SUM(s.amount_sold)
FROM   sales s, times t
WHERE s.time_id = t.time_id AND t.week_ending_day BETWEEN TO_DATE
('01-AUG-1999', 'DD-MON-YYYY') AND TO_DATE('10-AUG-1999', 'DD-MON-YYYY')
GROUP BY week_ending_day;

マテリアライズド・ビューjoin_sales_time_product_mvは、salesとproductsの間の結合(s.prod_id=p.prod_id)が追加されています。これは、join_sales_time_product_mvのデルタ結合です。この結合が可逆式および非重複である場合は、問合せをリライトできます。s.prod_idがp.prod_idに対する外部キーで、かつ、NULLではない場合がその例です。したがって、問合せは次のようにリライトされます。

SELECT week_ending_day, SUM(amount_sold)
FROM   join_sales_time_product_mv
WHERE  week_ending_day BETWEEN TO_DATE('01-AUG-1999', 'DD-MON-YYYY')
AND     TO_DATE('10-AUG-1999', 'DD-MON-YYYY')
GROUP BY week_ending_day;

問合せは、外部キー制約が必要とされないマテリアライズド・ビューjoin_sales_time_product_mv_ojを使用してリライトすることもできます。このビューには、salesとproductsの間の外部結合(s.prod_id=p.prod_id(+))が含まれています。このため、この結合は可逆式になります。p.prod_idが主キーの場合、非重複条件も満たされ、オプティマイザは問合せを次のようにリライトします。

SELECT week_ending_day, SUM(amount_sold)
FROM   join_sales_time_product_oj_mv
WHERE  week_ending_day BETWEEN TO_DATE('01-AUG-1999', 'DD-MON-YYYY')
AND  TO_DATE('10-AUG-1999', 'DD-MON-YYYY')
GROUP BY week_ending_day;

問合せは、外部キー制約が必要とされないマテリアライズド・ビューjoin_sales_time_product_mv_ojを使用してリライトすることもできます。このビューには、salesとproductsの間の外部結合(s.prod_id=p.prod_id(+))が含まれています。このため、この結合は可逆式になります。p.prod_idが主キーの場合、非重複条件も満たされ、オプティマイザは問合せを次のようにリライトします。

SELECT week_ending_day, SUM(amount_sold)
FROM   join_sales_time_product_oj_mv
WHERE  week_ending_day BETWEEN TO_DATE('01-AUG-1999', 'DD-MON-YYYY')
AND  TO_DATE('10-AUG-1999', 'DD-MON-YYYY')
GROUP BY week_ending_day;

shスキーマでは、salesとproductsの間の主キー/外部キーの関係がすでに可逆式になっているため、join_sales_time_product_mv_ojの定義には外部結合が不要であることに注意してください。ただしこれは、単に具体例を示すためのものです。sales.prod_idがNULL値可能で、したがって結合条件sales.prod_id = products.prod_idに可逆性がなくなる場合、外部結合は必要となります。

現在の制限事項では、外部結合を含むリライトのほとんどは、結合のみを使用したマテリアライズド・ビューに制限されます。外部結合を含むマテリアライズド集計ビューを使用したリライトのサポートは制限されているため、この種のマテリアライズド・ビューは、マテリアライズド・ビューのデルタ結合の可逆性を保証するために、外部キー制約に依存する必要があります。

結合の等価性の認識

クエリー・リライトは、等価な結合であるとの認識に基づいて多くの変換を行うことができます。クエリー・リライトでは、次の構成体は結合に対して等価であると認識されます。

WHERE table1.column1 = F(args)   /* sub-expression A */
AND table2.column2 = F(args)     /* sub-expression B */

F(args)がDETERMINISTICとして宣言されているPL/SQLファンクションで、Fを起動するときの引数がいずれも同じである場合、副次式Aと副次式Bの組合せは、table1.column1とtable2.column2の結合として認識できます。つまり、次の式は、前述の式と等価になります。

WHERE table1.column1 = F(args)          /* sub-expression A */
AND table2.column2 = F(args)            /* sub-expression B */
AND table1.column1 = table2.column2     /* join-expression J */

結合式Jは副次式Aと副次式Bから推論できるため、推論された結合を使用して、マテリアライズド・ビュー内の対応する結合table1.column1 = table2.column2に一致させることができます。

データ充足性チェック

このチェックでは、オプティマイザは、問合せが要求した列データが、マテリアライズド・ビューから取得可能かどうかを判断します。このために、1つの列と別の列との同等化が使用されます。たとえば、表Aと表Bの間の内部結合が結合述部A.X = B.Xに基づく場合、結合の結果、列A.Xのデータは列B.Xのデータと同等になります。このデータ・プロパティが、問合せ内の列A.Xとマテリアライズド・ビュー内の列B.Xの一致、またはその逆に使用されます。たとえば、次の問合せを考えてみます。

SELECT p.prod_name, s.time_id, t.week_ending_day, SUM(s.amount_sold)
FROM sales s, products p, times t
WHERE s.time_id=t.time_id AND s.prod_id = p.prod_id
GROUP BY p.prod_name, s.time_id, t.week_ending_day;

この問合せは、マテリアライズド・ビューにs.time_idが含まれない場合でも、join_sales_time_product_mvを使用して回答されます。かわりにこの問合せには、結合条件s.time_id=t.time_idを介してs.time_idと同等のt.time_idが含まれます。したがって、オプティマイザで次のリライトが選択される場合があります。

SELECT prod_name, time_id, week_ending_day, SUM(amount_sold)
FROM join_sales_time_product_mv
GROUP BY prod_name, time_id, week_ending_day;

グルーピング互換性チェック

このチェックは、マテリアライズド・ビューと問合せの両方にGROUP BY句がある場合にのみ必要です。オプティマイザは、まず、問合せが要求したデータのグルーピングが、マテリアライズド・ビューに格納されているデータのグルーピングと同じかどうかを判断します。つまり、グルーピングのレベルは、問合せとマテリアライズド・ビューで同じです。マテリアライズド・ビューで問合せの列および式をすべてグルーピングし、追加の列または式もグルーピングする場合、クエリー・リライトでは、問合せのグルーピング列およびグルーピング式でマテリアライズド・ビューを再集計して、問合せで要求された同じ結果を導出できます。

集計可能性チェック

このチェックは、問合せおよびマテリアライズド・ビューの両方に集計が含まれている場合にのみ必要になります。このチェックでは、オプティマイザは、問合せが要求した集計が、マテリアライズド・ビューに格納された1つ以上の集計から導出または計算可能かどうかを判断します。たとえば、問合せがAVG(X)を要求し、マテリアライズド・ビューがSUM(X)およびCOUNT(X)を含む場合、AVG(X)はSUM(X)/COUNT(X)で計算できます。

グルーピング互換性チェックによって、マテリアライズド・ビューに格納された集計のロールアップが必要であると判断された場合、次に、集計可能性チェックによって、問合せが要求した各集計が、マテリアライズド・ビューの集計を使用してロールアップできるかどうかが判断されます。

ディメンションを使用したクエリー・リライト

この項では、リライト環境でディメンションを使用する際の次の側面について説明します。

- ディメンションを使用するメリット

- ディメンションの定義方法

ディメンションを使用するメリット

ディメンションは、列同士の階層(親子)関係を定義します。これらの列は、同じ表の列である必要はありません。

ディメンションを定義すると、列間の機能依存性を確立できるため、クエリー・リライトの実行性が高まります。また、ディメンションにより、制約では表現できない表内の関係を表現することもできます。ディメンション定義に追加の領域は必要ありません。むしろ、ディメンション定義を行うと、使用しているスキーマ内のディメンション内およびディメンション間の関係を記述するメタデータが構築されます。マテリアライズド・ビューを作成する前に行う最初の手順は、スキーマを調べてディメンションを定義することです。これによって問合せのリライトの機会が大幅に増す場合があるためです。

ディメンションの定義方法

ディメンションは、任意のスキーマに対し次の手順に従って作成できます。

手順1   スキーマ内のすべてのディメンションおよびディメンション表を指定する。

複数の表に収まるようにディメンションが正規化されている場合、ディメンション表間の結合において、子表の各行が親表の1つの行のみと結合していることが保証されていることを確認します。また非正規化ディメンションの場合、子の列が親(または属性)列を一意に決定できなくてはなりません。これらの規則を順守できなかった場合、問合せから不正確な結果が戻される場合があります。

手順2   各ディメンション内の階層を特定する。

たとえば、日(day)は月(month)の子(日レベルのデータを月レベルまで集計可能)であり、四半期(quarter)は年(year)の子です。

手順3   階層の各レベル内の属性依存性を特定する。

たとえば、calendar_month_nameが月の属性であることを指定します。

手順4   データ・ウェアハウスのファクト表から各ディメンションへの結合を特定する。

次に、各結合において、ファクト表の各行がディメンションの1つの行のみと結合することが保証されることを確認します。この条件を宣言し、オプションで規定する必要があります。それには、ファクト表のキー列に外部キー制約およびNOT NULL制約を追加し、親表の結合キーに主キー制約を追加します。これらの関係が、プロシージャ(ロード・プロセスなど)を処理する他のデータによって保証される場合、これらの制約は、NOVALIDATEオプションを使用して有効化できます。このオプションを使用すると、表のすべての行がその制約に準拠することを検証するのに必要な時間を節約できます。妥当性チェックが行われないすべての制約について、クエリー・リライトで使用できるようにするためには、RELY句も必要になります。

時間ディメンションを作成するSQL文の例

CREATE DIMENSION times_dim
LEVEL day IS TIMES.TIME_ID
LEVEL month IS TIMES.CALENDAR_MONTH_DESC
LEVEL quarter IS TIMES.CALENDAR_QUARTER_DESC
LEVEL year IS TIMES.CALENDAR_YEAR
LEVEL fis_week IS TIMES.WEEK_ENDING_DAY
LEVEL fis_month  IS TIMES.FISCAL_MONTH_DESC
LEVEL fis_quarter IS TIMES.FISCAL_QUARTER_DESC
LEVEL fis_year IS TIMES.FISCAL_YEAR
HIERARCHY cal_rollup
(day  CHILD OF month CHILD OF quarter CHILD OF year)
HIERARCHY fis_rollup
(day  CHILD OF fis_week CHILD OF fis_month CHILD OF fis_quarter
CHILD OF fis_year)

ATTRIBUTE day DETERMINES
(day_number_in_week, day_name, day_number_in_month,
calendar_week_number)

ATTRIBUTE month DETERMINES
(calendar_month_desc, calendar_month_number, calendar_month_name,
days_in_cal_month, end_of_cal_month)

ATTRIBUTE quarter DETERMINES
(calendar_quarter_desc, calendar_quarter_number,days_in_cal_quarter,
end_of_cal_quarter)

ATTRIBUTE year DETERMINES
(calendar_year,  days_in_cal_year, end_of_cal_year)

ATTRIBUTE fis_week DETERMINES
(week_ending_day, fiscal_week_number);

ディメンションで宣言された関係をクエリー・リライトで利用するには、パラメータQUERY_REWRITE_INTEGRITYをTRUSTEDまたはSTALE_TOLERATEDに設定する必要があります。

クエリー・リライトのタイプ

多数の行または大規模な表同士の結合に対して計算が必要な集計を持つ問合せは、コストが高くなる可能性があり、結果を戻すまでに長い時間がかかる場合があります。クエリー・リライトは、事前計算済の結果が格納されているマテリアライズド・ビューを使用してそのような問合せを透過的にリライトするので、問合せに対して瞬時に結果を戻すことができます。これらのマテリアライズド・ビューは、大きく2つのグループに分類できます。すなわち、マテリアライズド集計ビューとマテリアライズド結合ビューです。マテリアライズド集計ビューは、元の表の列の事前に計算された集計値を格納する表です。同様に、マテリアライズド結合ビューは、元の表の列間の事前に計算された結合を格納する表です。クエリー・リライトは、入力問合せを変換して、マテリアライズド・ビューの列から結果をフェッチします。これらの列には事前に計算された結果がすでに格納されているので、入力問合せに対して瞬時に結果を戻すことができます。キューブ・マテリアライズド・ビューのクエリー・リライトに関する考慮事柄については、『Oracle OLAPユーザーズ・ガイド』を参照してください。

この項では、問合せのリライトに使用できる次の方法について説明します。

- テキスト一致リライト

- 後戻り結合

- 集計可能性

- 集計ロールアップ

- ディメンションを使用したロールアップ

- マテリアライズド・ビューに含まれるデータのサブセットが1つのみの場合

- パーティション・チェンジ・トラッキング(PCT)リライト

- 複数のマテリアライズド・ビュー

テキスト一致リライト

クエリー・リライト・エンジンは常に、問合せをリライトする際に対象となり得るすべてのマテリアライズド・ビューの定義のテキストと、入力問合せのテキストとの比較を最初に試みます。これは、一般的なリライトに必要な複雑な分析を行うコストと比較して、単純なテキスト比較を行うオーバーヘッドが通常わずかであるためです。

クエリー・リライト・エンジンは、テキストの完全一致のリライトおよびテキストの部分一致によるリライトという、2つのテキスト一致方法を使用します。テキストが完全に一致する場合は、問合せのテキスト全体がマテリアライズド・ビュー定義のテキスト全体(SELECT文全体)と比較されます。テキストの比較中は、空白は無視されます。たとえば、次のマテリアライズド・ビューsum_sales_pscat_month_city_mvがあるとします。

CREATE MATERIALIZED VIEW sum_sales_pscat_month_city_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_subcategory, t.calendar_month_desc, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold,
COUNT(s.amount_sold) AS count_amount_sold
FROM sales s, products p, times t, customers c
WHERE s.time_id=t.time_id
AND     s.prod_id=p.prod_id
AND     s.cust_id=c.cust_id
GROUP BY p.prod_subcategory, t.calendar_month_desc, c.cust_city;

次の問合せを考えてみます。

SELECT p.prod_subcategory, t.calendar_month_desc, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold,
COUNT(s.amount_sold) AS count_amount_sold
FROM sales s, products p, times t, customers c
WHERE s.time_id=t.time_id
AND     s.prod_id=p.prod_id
AND     s.cust_id=c.cust_id
GROUP BY p.prod_subcategory, t.calendar_month_desc, c.cust_city;

この問合せは、sum_sales_pscat_month_city_mv(空白は除外)と一致し、次のようにリライトされます。

SELECT mv.prod_subcategory, mv.calendar_month_desc, mv.cust_city,
mv.sum_amount_sold, mv.count_amount_sold
FROM   sum_sales_pscat_month_city_mv;

テキストの完全一致が失敗した場合、オプティマイザはテキストの部分一致を試みます。この方法では、問合せのFROM句から始まるテキストが、マテリアライズド・ビュー定義のFROM句から始まるテキストと比較されます。したがって、次の問合せはリライトできます。

SELECT p.prod_subcategory, t.calendar_month_desc, c.cust_city,
AVG(s.amount_sold)
FROM   sales s, products p, times t, customers c
WHERE  s.time_id=t.time_id AND s.prod_id=p.prod_id
AND    s.cust_id=c.cust_id
GROUP BY p.prod_subcategory, t.calendar_month_desc, c.cust_city;

この問合せは、次のようにリライトされます。

SELECT mv.prod_subcategory, mv.calendar_month_desc, mv.cust_city,
mv.sum_amount_sold/mv.count_amount_sold
FROM   sum_sales_pscat_month_city_mv mv;

テキストの部分一致によるリライト方法の場合、問合せが要求した売上集計の平均は、マテリアライズド・ビューに格納された売上の合計および売上集計の件数を使用して計算されます。

どちらのテキストの一致も成功しなかった場合、オプティマイザは、一般的なクエリー・リライト方法を使用します。

テキスト一致リライトでは、大文字と小文字の違いに意味があるコンテキストとそうでないコンテキストが区別されます。たとえば、次の2つの文は同等です。

SELECT X, 'aBc' FROM Y

Select x, 'aBc' From y

後戻り結合

問合せが要求した列データがマテリアライズド・ビューから取得できない場合、オプティマイザは、さらに、機能依存性と呼ばれるデータ関係を基に取得できないかを判断します。列内のデータによって別の列のデータを決定できる場合、そのような関係は、機能依存性または機能決定性と呼ばれます。たとえば、1つの表にprod_idという主キー列、およびprod_nameという別の列があるとします。prod_id値を指定した場合、対応付けられたprod_nameを参照できます。この逆は真ではありません。つまり、prod_name値は一意のprod_idに関連付けられている必要はありません。

問合せが要求した列データが、マテリアライズド・ビューに含まれない場合、要求された列データを機能的に決定するキーがマテリアライズド・ビューに含まれていれば、それらの列データは、要求された列データを含む表をマテリアライズド・ビューに後戻り結合することによって取得できます。たとえば、次の問合せを考えてみます。

SELECT p.prod_category, t.week_ending_day, SUM(s.amount_sold)
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id  AND s.prod_id=p.prod_id AND p.prod_category='CD'
GROUP BY p.prod_category, t.week_ending_day;

マテリアライズド・ビューsum_sales_prod_week_mvにはp.prod_idが含まれていますが、p.prod_categoryは含まれていません。ただし、prod_idは機能的にprod_categoryを決定するため、sum_sales_prod_week_mvをproductsに再び結合して、prod_categoryを取り出すことができます。オプティマイザは、sum_sales_prod_week_mvを次のように使用して、この問合せをリライトします。

SELECT p.prod_category, mv.week_ending_day, SUM(mv.sum_amount_sold)
FROM   sum_sales_prod_week_mv mv, products p
WHERE  mv.prod_id=p.prod_id AND p.prod_category='CD'
GROUP BY p.prod_category, mv.week_ending_day;

この場合、products表は、マテリアライズド・ビューに結合されていたものが、リライトされた問合せで再び結合されたため、後戻り結合表と呼ばれます。

機能依存性は、次の2つの方法で宣言できます。

- 主キー制約を使用する方法(前述の例を参照)

- ディメンションのDETERMINES句を使用する方法

別の列を決定する列を主キーにできない場合、ディメンション定義のDETERMINES句のみが、機能依存性を宣言できる唯一の方法になることがあります。たとえば、products表は、prod_id、prod_nameおよびprod_subcategoryの各列を持つ非正規化ディメンション表です。prod_subcategoryはprod_subcat_descおよびprod_categoryを機能的に決定します。また、prod_categoryは、prod_cat_descを機能的に決定します。

最初の機能依存性は、prod_idを主キーとして宣言することで確立されますが、2番目の機能依存性は、prod_subcategory列に重複値が含まれるため、この方法では確立できません。そのような場合は、ディメンションのDETERMINES句を使用すると、2番目の機能依存性を宣言できます。

次のディメンション定義は、機能依存性の宣言方法を示します。

CREATE DIMENSION products_dim
LEVEL product           IS (products.prod_id)
LEVEL subcategory       IS (products.prod_subcategory)
LEVEL category          IS (products.prod_category)
HIERARCHY prod_rollup (
product         CHILD OF
subcategory     CHILD OF
category
)
ATTRIBUTE product DETERMINES products.prod_name
ATTRIBUTE product DETERMINES products.prod_desc
ATTRIBUTE subcategory DETERMINES products.prod_subcat_desc
ATTRIBUTE category DETERMINES products.prod_cat_desc;

階層prod_rollupは、1:n機能依存性でもある階層関係を宣言します。1:1機能依存性は、prod_subcategoryが機能的にprod_subcat_descを決定するように、DETERMINES句を使用して宣言されます。

次のマテリアライズド・ビューが作成されているものとします。

CREATE MATERIALIZED VIEW sum_sales_pscat_week_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_subcategory, t.week_ending_day,
SUM(s.amount_sold) AS sum_amount_sole
FROM sales s, products p, times t
WHERE s.time_id = t.time_id AND s.prod_id = p.prod_id
GROUP BY p.prod_subcategory, t.week_ending_day;

次の問合せを考えてみます。

SELECT p.prod_subcategory_desc, t.week_ending_day, SUM(s.amount_sold)
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id AND s.prod_id=p.prod_id
AND    p.prod_subcat_desc LIKE '%Men'
GROUP BY p.prod_subcat_desc, t.week_ending_day;

この問合せは、sum_sales_pscat_week_mvをproducts表に結合することによりリライトできます。これにより、prod_subcat_descを使用して選択述語の評価を行うことが可能になります。ただし、結合は、products表の主キーでないprod_subcategory列をベースにするため、重複が許可されます。これは、個別値を選択するインライン・ビューを使用することによって実現され、このビューは、次のリライトされた問合せで示すように、マテリアライズド・ビューと結合されます。

SELECT iv.prod_subcat_desc, mv.week_ending_day, SUM(mv.sum_amount_sold)
FROM  sum_sales_pscat_week_mv mv,
(SELECT DISTINCT prod_subcategory, prod_subcat_desc
FROM products) iv
WHERE  mv.prod_subcategory=iv.prod_subcategory
AND iv.prod_subcat_desc LIKE '%Men'
GROUP BY iv.prod_subcat_desc, mv.week_ending_day;

このようなリライトが可能なのは、prod_subcategoryが、ディメンションで宣言されたようにprod_subcategory_descを機能的に決定することによります。

集計可能性

問合せで要求された集計がマテリアライズド・ビューに格納された1つ以上の集計から導出または計算可能かどうかをオプティマイザで判断する場合にも、クエリー・リライトは可能です。たとえば、問合せがAVG(X)を要求し、マテリアライズド・ビューがSUM(X)およびCOUNT(X)を含む場合、AVG(X)はSUM(X)/COUNT(X)で計算できます。

また、マテリアライズド・ビューに格納された集計のロールアップが必要と判断され、ロールアップが可能な場合、クエリー・リライトでは、マテリアライズド・ビューの集計を使用して、問合せで要求された各集計もロールアップします。

たとえば、州の値が同じグループのSUM(sales)集計をすべて合計することによって、市レベルのSUM(sales)を州レベルのSUM(sales)にロールアップできます。ただし、COUNT(sales)またはSUM(sales)がマテリアライズド・ビューで使用可能でない場合、細分性が低いレベルにAVG(sales)をロールアップすることはできません。同様に、COUNT(sales)およびSUM(sales)がマテリアライズド・ビューで使用可能でない場合、VARIANCE(sales)またはSTDDEV(sales)はロールアップできません。たとえば、次の問合せを考えてみます。

ALTER TABLE times MODIFY CONSTRAINT time_pk RELY;
ALTER TABLE customers MODIFY CONSTRAINT customers_pk RELY;
ALTER TABLE sales MODIFY CONSTRAINT sales_time_pk RELY;
ALTER TABLE sales MODIFY CONSTRAINT sales_customer_fk RELY;
SELECT  p.prod_subcategory, AVG(s.amount_sold) AS avg_sales
FROM  sales s, products p WHERE s.prod_id = p.prod_id
GROUP BY p.prod_subcategory;

salesとtimes、およびsalesとcustomersの結合が可逆式で非重複の場合は、この文では、マテリアライズド・ビューsum_sales_pscat_month_city_mvをリライトに使用できます。さらに、問合せはprod_subcategoryによるグルーピング、マテリアライズド・ビューはprod_subcategory、calendar_month_descおよびcust_cityによるグルーピングであるため、マテリアライズド・ビューに格納された集計がロールアップされる必要があります。オプティマイザは、次のように問合せをリライトします。

SELECT mv.prod_subcategory, SUM(mv.sum_amount_sold)/COUNT(mv.count_amount_sold)
AS avg_sales
FROM sum_sales_pscat_month_city_mv mv
GROUP BY mv.prod_subcategory;

SUMのような集計には、A+Bなどの算術式を引数とすることができます。オプティマイザでは、問合せ内の集計SUM(A+B)と、マテリアライズド・ビューに格納されている集計SUM(A+B)またはSUM(B+A)とを一致させるよう処理します。つまり、問合せ内の集計の引数とマテリアライズド・ビューの同様の集計の引数を一致させる際に、式の同等化を使用します。そのためには、同等である別々の2つの式が同じ標準形になるように、Oracleは集計引数式を標準的な形式に変換します。たとえば、A*(B-C)、A*B-C*A、(B-C)*Aおよび-A*C+A*Bはすべて同一の標準形に変換され、それによってこれらは正しく一致します。

集計ロールアップ

問合せが要求したデータのグルーピングが、マテリアライズド・ビューに格納されているデータのグルーピングより細分性が低いレベルにある場合でも、オプティマイザはマテリアライズド・ビューを使用して問合せをリライトできます。たとえば、マテリアライズド・ビューsum_sales_pscat_week_mvは、prod_subcategoryとweek_ending_dayによるグルーピングです。この問合せは、prod_subcategoryによるグルーピング(より細分性が低いグルーピング)です。

ALTER TABLE times MODIFY CONSTRAINT time_pk RELY;
ALTER TABLE sales MODIFY CONSTRAINT sales_time_fk RELY;
SELECT p.prod_subcategory, SUM(s.amount_sold) AS sum_amount
FROM   sales s, products pWHERE  s.prod_id=p.prod_id
GROUP BY p.prod_subcategory;

したがって、オプティマイザは次のようにこの問合せをリライトします。

SELECT mv.prod_subcategory, SUM(mv.sum_amount_sold)
FROM   sum_sales_pscat_week_mv mv
GROUP BY mv.prod_subcategory;

ディメンションを使用したロールアップ

階層の異なるレベルでレポートが必要な場合、ディメンションが定義済であれば、階層内のレベルごとにマテリアライズド・ビューを作成する必要はありません。これは、クエリー・リライトが、ディメンション内の関係情報を使用して、マテリアライズド・ビューのデータを階層内の必要なレベルにロールアップできるためです。

次の例では、問合せはprod_categoryでグルーピングされたデータを要求し、マテリアライズド・ビューはprod_subcategoryでグルーピングされたデータを格納しています。prod_subcategoryがCHILD OF prod_categoryである場合(前述のディメンション例を参照)、マテリアライズド・ビューに格納されているグルーピングされたデータは、問合せがリライトされた場合にprod_categoryによってさらにグルーピングできます。つまり、マテリアライズド・ビューに格納されたprod_subcategoryレベルの(細分性がより高い)集計は、prod_categoryレベルの(細分性がより低い)集計にロールアップできます。

たとえば、次の問合せを考えてみます。

SELECT p.prod_category, t.week_ending_day, SUM(s.amount_sold) AS sum_amount
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id AND s.prod_id=p.prod_id
GROUP BY p.prod_category, t.week_ending_day;

prod_subcategoryは機能的にprod_categoryを決定するため、sum_sales_pscat_week_mvは、prod_category列データを取り出すためにproducts表の後戻り結合で使用されます。その後、集計は、次に示すようにprod_categoryレベルにロールアップされます。

SELECT pv.prod_category, mv.week_ending_day, SUM(mv.sum_amount_sold)
FROM   sum_sales_pscat_week_mv mv,
(SELECT DISTINCT prod_subcategory, prod_category
FROM products) pv
WHERE mv.prod_subcategory= pv.prod_subcategory
GROUP BY pv.prod_category, mv.week_ending_day;

マテリアライズド・ビューに含まれるデータのサブセットが1つのみの場合

Oracleではクエリー・リライトに、HAVING句またはWHERE句によって1つの表または複数の表からデータを絞り込んでいるマテリアライズド・ビューを使用することをサポートします。たとえば、New Hampshire在住の顧客に限定したものなどです。つまりWHERE句は、WHERE state = 'New Hampshire'となります。

このタイプのクエリー・リライトを実行するには、Oracleは、問合せで要求されたデータがマテリアライズド・ビューに格納されているデータに含まれているか、そのサブセットかどうかを判断する必要があります。以降の項では、Oracleでこの問題が解決され、ディテール表のデータのフィルタ処理済部分を含むマテリアライズド・ビューを使用するように問合せがリライトされる場合の条件について説明します。

フィルタ処理済データでクエリー・リライトを実行できるかどうかを判断するために、問合せとマテリアライズド・ビューの両方に選択述語(非結合)が含まれている場合に選択互換性チェックが実行されます。このチェックは、WHERE句およびHAVING句で行われます。マテリアライズド・ビューに選択が含まれ、問合せに含まれていなければ、マテリアライズド・ビューの方が問合せより限定的であるため、選択互換性チェックは失敗します。問合せに選択述語があり、マテリアライズド・ビューになければ、選択互換性チェックは不要です。

マテリアライズド・ビューのWHEREまたはHAVING句は、結合または絞込み選択(あるいはその両方)を使用でき、問合せのリライトにも使用されます。式を含む述語句や、特定の列の値に基づいて行を選択する述語句は、非結合述語の一例です。

クエリー・リライトの定義

クエリー・リライトでデータの1つのサブセットのみを処理する際に可能な事柄について説明する前に、次の定義について説明します。

- join relop
(=、<、<=、>、>=)のいずれかです。

- selection relop
(=、<、<=、>、>=、!=、[NOT] BETWEEN | IN| LIKE |NULL)のいずれかです。

- join predicate
(column1 join relop column2)形式です。列は、現在の問合せブロックで同じFROM句に記述された異なる表の列です。したがって、外部参照などは使用できません。

- selection predicate
左辺式relop右辺式、の形式です。すべての非結合述語は選択述語です。通常、左辺には列、右辺には値が含まれます。たとえば、color='red'の場合、左辺はcolor、右辺は'red'、関係演算子は(=)です。

選択述語のカテゴリ

選択述語は、次のように分類されます。

- 単純
単純な選択述語は、expression relop constantという形式です。

- 複雑
複雑な選択述語は、expression relop expressionという形式です。

- 範囲
範囲選択述語は、WHERE (cust_last_name BETWEEN 'abacrombe' AND 'anakin')などの形式です。

関係演算子(<、<=、>、>=)を含む単純な選択述語も、範囲選択述語とみなされます。

- INリスト
WHERE(prod_id) IN (102, 233, ....)など、単一列と複数列のINリストです。

(column1='v1' OR column1='v2' OR column1='v3' OR ....)形式の選択は、グループとして扱われ、INリストに分類されます。

- IS [NOT] NULL

- [NOT] LIKE

- その他
その他の選択述語は、データの境界を判断できない場合です。たとえば、EXISTSなどです。

問合せの選択述語とマテリアライズド・ビューの選択述語の比較では、両者の選択述語の左辺が比較されます。

左辺の選択述語が一致した場合、右辺値でデータの包含がチェックされます。つまり、問合せの選択述語の右辺値が、マテリアライズド・ビューの選択述語の右辺値に含まれている必要があります。

選択述語で式を使用することもできます。この処理は次のようになります。

expression relational operator constant

expressionには、Oracle Databaseで許可されている任意の算術式を使用できます。マテリアライズド・ビューと問合せの式は一致する必要があります。Oracleでは、A+BやB+Aなど、論理的に等価の式は常に同じ式として認識されます。

また、演算子または演算子として定義されるユーザー定義関数の左辺と右辺に式を持つ問合せを使用することもできます。クエリー・リライトは、マテリアライズド・ビューと問合せの複雑な選択述語が論理的に等価のときに発生します。これは、テキストの完全一致とは異なり、式が等価であるかぎり、条件を異なる順序で指定してもリライトできることを意味します。

クエリー・リライトの選択述語の例

次に、データがフィルタ処理される際にクエリー・リライトが可能な様々な例を示します。

例19-1 単一値の選択述語

問合せに次の句が含まれているとします。

WHERE prod_id = 102

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE prod_id BETWEEN 0 AND 200

この例では、左辺の選択述語がprod_idで一致し、問合せの右辺値102がマテリアライズド・ビューの範囲内にあるため、クエリー・リライトは可能です。

例19-2 境界付き範囲の選択述語

選択述語には、境界付き範囲(上限値と下限値を持つ範囲)を使用できます。たとえば、問合せに次の句が含まれているとします。

WHERE prod_id > 10 AND prod_id < 50

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE prod_id BETWEEN 0 AND 200

この場合、選択述語はprod_idで一致し、問合せの範囲はマテリアライズド・ビューの範囲内にあります。この例では、問合せの選択が両方とも同じ列に基づいていることがわかります。

例19-3 式を使用した選択述語

問合せに次の句が含まれているとします。

WHERE (sales.amount_sold * .07) BETWEEN 1.00 AND 100.00

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE (sales.amount_sold * .07) BETWEEN 0.0 AND 200.00

この例では、選択述語が(sales.amount_sold *.07)で一致し、問合せの右辺値がマテリアライズド・ビューの範囲内にあるため、クエリー・リライトは可能です。このような複雑な選択述語では、左辺と右辺がマテリアライズド・ビューの範囲内で一致している必要があります。

例19-4 完全一致の選択述語

問合せに次の句が含まれているとします。

WHERE (cost.unit_price * 0.95) > (cost_unit_cost  * 1.25)

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE (cost.unit_price * 0.95) > (cost_unit_cost  * 1.25)

左辺と右辺がマテリアライズド・ビューに一致し、selection_relopが同一の場合、通常はリライトされた問合せから選択述語を削除できます。それ以外の場合は、選択述語によりマテリアライズド・ビューから余分なデータを除外する必要があります。

クエリー・リライトで、リライトされた問合せから選択述語を削除できる場合は、選択述語のすべての列がマテリアライズド・ビュー内になくてもかまわないため、より多くのリライトを実行できます。これにより、マテリアライズド・ビューのデータが問合せより限定的でないことが保証されます。

例19-5 問合せでの追加選択述語

問合せの選択述語はマテリアライズド・ビューの選択述語と一致する必要はありませんが、一致する場合は、右辺値がマテリアライズド・ビューに含まれている必要があります。たとえば、問合せに次の句が含まれているとします。

WHERE prod_name = 'Shorts' AND prod_category = 'Men'

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE prod_category = 'Men'

この例では、prod_categoryのみが選択述語一致です。問合せには、マテリアライズド・ビューとは一致しない選択述語がありますが、マテリアライズド・ビューで、prod_nameを選択するか、ディテール表に後戻り結合してprod_nameを取得できる列を選択する場合は許容され、クエリー・リライトが可能です。唯一の要件は、選択述語prod_nameをマテリアライズド・ビューに適用する方法がクエリー・リライトに必要とされることです。

例19-6 問合せ内の選択述語が少ないためにリライトが発生しない場合

問合せに次の句が含まれているとします。

WHERE prod_category = 'Men'

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE prod_name = 'Shorts' AND prod_category = 'Men'

この例では、マテリアライズド・ビューの選択述語prod_nameが一致しません。マテリアライズド・ビューのみが製品Shortsを含んでいるので、マテリアライズド・ビューの方が問合せより限定的です。したがって、クエリー・リライトは発生しません。

例19-7 複数列のINリストの選択述語

クエリー・リライトでは、問合せに複数列のINリストがあり、その各列がマテリアライズド・ビューの単一列のINリストからの個々の列と完全に一致している場合も、チェックされます。たとえば、問合せに次の句が含まれているとします。

WHERE (prod_id, cust_id) IN ((1022, 1000), (1033, 2000))

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE prod_id IN (1022,1033) AND cust_id IN (1000, 2000)

この例では、マテリアライズド・ビューのINリストは、問合せの複数列のINリスト内の列と一致しています。さらに、問合せの選択の右辺値は、マテリアライズド・ビューに含まれているため、リライトが発生します。

例19-8 INリストを使用した選択述語

選択互換性では、マテリアライズド・ビューに複数列のINリストがあり、その各列が問合せ内のINリストの1つ以上の列と完全に一致している場合も、チェックされます。たとえば、問合せに次の句が含まれているとします。

WHERE prod_id = 1022 AND cust_id IN (1000, 2000)

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE (prod_id, cust_id) IN ((1022, 1000), (1022, 2000))

この例では、マテリアライズド・ビューのINリストの列は、問合せの選択述語の列と一致しています。さらに、問合せの選択述語の右辺値は、マテリアライズド・ビューに含まれています。したがって、リライトは成功します。

例19-9 複数の選択述語または式

問合せに次の句が含まれているとします。

WHERE (city_population > 15000 AND city_population < 25000
AND state_name = 'New Hampshire')

また、マテリアライズド・ビューに次の句が含まれているとします。

WHERE (city_population < 5000 AND state_name = 'New York') OR
(city_population BETWEEN 10000 AND 50000 AND state_name = 'New Hampshire')

この例では、問合せに単一の離接詞(ANDで区切られた選択述語のグループ)があります。また、マテリアライズド・ビューには、ORで区切られた離接詞が2つあります。問合せの単一の離接詞はマテリアライズド・ビューの2番目の離接詞に含まれているため、選択互換性チェックにパスします。マテリアライズド・ビューには、問合せで必要とするより多くのデータが含まれているため、問合せをリライトできることは明らかです。

クエリー・リライトでのHAVING句の処理

問合せで指定された範囲がマテリアライズド・ビューで指定された範囲内にあれば、SUM(s.amount_sold) BETWEEN 10000 AND 20000のように、問合せのHAVING句で集計値の範囲が指定されている場合でも、クエリー・リライトが可能です。

CREATE MATERIALIZED VIEW product_sales_mv
BUILD IMMEDIATE
REFRESH FORCE
ENABLE QUERY REWRITE AS
SELECT p.prod_name, SUM(s.amount_sold) AS dollar_sales
FROM products p, sales s
WHERE p.prod_id = s.prod_id
GROUP BY prod_name
HAVING SUM(s.amount_sold) BETWEEN 5000 AND 50000;

したがって、次の問合せはリライトできます。

SELECT p.prod_name, SUM(s.amount_sold) AS dollar_sales
FROM products p, sales s WHERE p.prod_id = s.prod_id
GROUP BY prod_name
HAVING SUM(s.amount_sold) BETWEEN 10000 AND 20000;

この問合せは、次のようにリライトされます。

SELECT mv.prod_name, mv.dollar_sales FROM product_sales_mv mv
WHERE mv.dollar_sales BETWEEN 10000 AND 20000;

マテリアライズド・ビューにINリストが含まれる場合のクエリー・リライト

マテリアライズド・ビューにINリストが含まれる際、クエリー・リライトを使用できます。たとえば、次のマテリアライズド・ビューの定義があるとします。

CREATE MATERIALIZED VIEW popular_promo_sales_mv
BUILD IMMEDIATE
REFRESH FORCE
ENABLE QUERY REWRITE AS
SELECT p.promo_name, SUM(s.amount_sold) AS sum_amount_sold
FROM  promotions p, sales s
WHERE s.promo_id = p.promo_id
AND p.promo_name IN ('coupon', 'premium', 'giveaway')
GROUP BY promo_name;

次の問合せはリライトできます。

SELECT p.promo_name, SUM(s.amount_sold)
FROM  promotions p, sales s
WHERE s.promo_id = p.promo_id AND p.promo_name IN ('coupon', 'premium')
GROUP BY p.promo_name;

この問合せは、次のようにリライトされます。

SELECT * FROM popular_promo_sales_mv mv
WHERE mv.promo_name IN ('coupon', 'premium');

パーティション・チェンジ・トラッキング(PCT)リライト

PCTリライトでは、オプティマイザは、一部のみが最新のマテリアライズド・ビューを使用して、問合せを最新のデータで正確にリライトできます。そのために、ディテール表内で更新されたパーティションが追跡されます。その次に、ディテール表内の更新されたパーティションに基づくマテリアライズド・ビュー内の行が追跡されます。これにより、オプティマイザは、マテリアライズド・ビューの最新と認識される部分を使用できるようになります。最新かどうかに関する詳細は、DBA_MVIEWSビュー、DBA_DETAIL_RELATIONSビューおよびDBA_MVIEW_DETAIL_PARTITIONビューで確認できます。これらのビューの使用例は「パーティションの最新状態の表示」を参照してください。

オプティマイザでは、QUERY_REWRITE_INTEGRITY = ENFORCEDモードまたはTRUSTEDモードでPCTリライトを使用します。STALE_TOLERATEDモードでは、データが最新かどうかが考慮されないため、PCTリライトは使用されません。また、PCTリライトが実行されるためにはWHERE句が必要です。

PCTリライトは、パーティション化を伴う場合でも使用できますが、ハッシュ・パーティション化はサポートされていません。次の項では、PCTの使用における側面について説明します。

- レンジ・パーティション表に基づいたPCTリライト

- レンジ-リスト・パーティション表に基づいたPCTリライト

- リスト・パーティション表に基づいたPCTリライト

- PCTリライトとPMARKER

- PMARKERとしてROWIDを使用したPCTリライト

レンジ・パーティション表に基づいたPCTリライト

次に示すのは、マテリアライズド・ビューのPCTがパーティション・キーで有効になっており、さらに基になる実表がtimeキーでレンジ・パーティション化されている場合のPCTリライトの例です。

CREATE TABLE part_sales_by_time (time_id, prod_id, amount_sold,
quantity_sold)
PARTITION BY RANGE (time_id)
(
PARTITION old_data
VALUES LESS THAN (TO_DATE('01-01-1999', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M),
PARTITION quarter1
VALUES LESS THAN (TO_DATE('01-04-1999', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M),
PARTITION quarter2
VALUES LESS THAN (TO_DATE('01-07-1999', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M),
PARTITION quarter3
VALUES LESS THAN (TO_DATE('01-10-1999', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M),
PARTITION quarter4
VALUES LESS THAN (TO_DATE('01-01-2000', 'DD-MM-YYYY'))
PCTFREE 0
STORAGE (INITIAL 8M),
PARTITION max_partition
VALUES LESS THAN (MAXVALUE)
PCTFREE 0
STORAGE (INITIAL 8M)
)
AS
SELECT s.time_id, s.prod_id, s.amount_sold, s.quantity_sold
FROM sales s;

ここで、1日ごとに販売された製品の総数を含むマテリアライズド・ビューを作成します。

CREATE MATERIALIZED VIEW  sales_in_1999_mv
BUILD IMMEDIATE
REFRESH FORCE ON DEMAND
ENABLE QUERY REWRITE
AS
SELECT s.time_id, s.prod_id, p.prod_name, SUM(quantity_sold)
FROM part_sales_by_time s, products p
WHERE p.prod_id = s.prod_id
AND s.time_id BETWEEN TO_DATE('01-01-1999', 'DD-MM-YYYY')
AND TO_DATE('31-12-1999', 'DD-MM-YYYY')
GROUP BY s.time_id, s.prod_id, p.prod_name;

次に示す問合せが、マテリアライズド・ビューsales_in_1999_mvでリライトされます。

SELECT s.time_id, p.prod_name, SUM(quantity_sold)
FROM part_sales_by_time s, products p
WHERE p.prod_id = s.prod_id
AND s.time_id < TO_DATE(''01-02-1999'', ''DD-MM-YYYY'')
AND s.time_id >= TO_DATE(''01-01-1999'', ''DD-MM-YYYY'')
GROUP BY s.time_id, p.prod_name');

part_sales_by_timeのquarter4に行を追加する場合は、次のようになります。

INSERT INTO part_sales_by_time
VALUES (TO_DATE('26-12-1999', 'DD-MM-YYYY'),38920,2500, 20);

commit;

このとき、マテリアライズド・ビューsales_in_1999_mvは失効状態になります。PCTリライトを使用すると、マテリアライズド・ビューの最新部分のデータのみを要求する問合せをリライトできます。マテリアライズド・ビューsales_in_1999_mvは、SELECT句およびGROUP BY句にtime_idが含まれています。したがってPCTは有効になっており、次の問合せはquarter4のデータを要求しないように正しくリライトされます。

SELECT s.time_id, p.prod_name, SUM(quantity_sold)
FROM part_sales_by_time s, products p
WHERE p.prod_id = s.prod_id
AND s.time_id < TO_DATE(''01-07-1999'', ''DD-MM-YYYY'')
AND s.time_id >= TO_DATE(''01-03-1999'', ''DD-MM-YYYY'')
GROUP BY s.time_id, p.prod_name');

複数のマテリアライズド・ビューによるリライトが無効になっている場合、次の問合せはリライトできません。デフォルトでは、複数のマテリアライズド・ビューによるリライトが有効になっているため、次の問合せはマテリアライズド・ビューと実表を使用してリライトされます。

SELECT s.time_id, p.prod_name, SUM(quantity_sold)
FROM part_sales_by_time s, products p
WHERE p.prod_id = s.prod_id
AND s.time_id < TO_DATE(''31-10-1999'', ''DD-MM-YYYY'') AND
s.time_id > TO_DATE(''01-07-1999'', ''DD-MM-YYYY'')
GROUP BY s.time_id, p.prod_name');

レンジ-リスト・パーティション表に基づいたPCTリライト

ディテール表がレンジ-リスト・パーティション化されている場合、このディテール表に依存するマテリアライズド・ビューは、パーティション化とサブパーティション化の両方のレベルでPCTをサポートできます。パーティション化キーとサブパーティション化キーの両方がマテリアライズド・ビューにある場合、より細かくPCTを実行できます。これにより、マテリアライズド・ビューのより細かな部分に対してマテリアライズド・ビューをリフレッシュでき、失効したマテリアライズド・ビューでより多くの問合せをリライトできるようになります。また、マテリアライズド・ビューにパーティション化キーしかない場合は、PCTは粗く実行されます。

次のレンジ-リスト・パーティション表を考えてみます。

CREATE TABLE sales_par_range_list
(calendar_year, calendar_month_number, day_number_in_month,
country_name, prod_id, prod_name, quantity_sold, amount_sold)
PARTITION BY RANGE (calendar_month_number)
SUBPARTITION BY LIST (country_name)
(PARTITION q1 VALUES LESS THAN (4)
(SUBPARTITION q1_America VALUES
('United States of America', 'Argentina'),
SUBPARTITION q1_Asia VALUES ('Japan', 'India'),
SUBPARTITION q1_Europe VALUES ('France', 'Spain', 'Ireland')),
PARTITION q2 VALUES LESS THAN (7)
(SUBPARTITION q2_America VALUES
('United States of America', 'Argentina'),
SUBPARTITION q2_Asia VALUES ('Japan', 'India'),
SUBPARTITION q2_Europe VALUES ('France', 'Spain', 'Ireland')),
PARTITION q3 VALUES LESS THAN (10)
(SUBPARTITION q3_America VALUES
('United States of America', 'Argentina'),
SUBPARTITION q3_Asia VALUES ('Japan', 'India'),
SUBPARTITION q3_Europe VALUES ('France', 'Spain', 'Ireland')),
PARTITION q4 VALUES LESS THAN (13)
(SUBPARTITION q4_America VALUES
('United States of America', 'Argentina'),
SUBPARTITION q4_Asia VALUES ('Japan', 'India'),
SUBPARTITION q4_Europe VALUES ('France', 'Spain', 'Ireland')))
AS SELECT t.calendar_year, t.calendar_month_number,
t.day_number_in_month, c1.country_name, s.prod_id,
p.prod_name, s.quantity_sold, s.amount_sold
FROM times t, countries c1, products p, sales s, customers c2
WHERE s.time_id = t.time_id AND s.prod_id = p.prod_id AND
s.cust_id = c2.cust_id AND c2.country_id = c1.country_id AND
c1.country_name IN ('United States of America', 'Argentina',
'Japan', 'India', 'France', 'Spain', 'Ireland');

製品に関する各年の月ごとの合計売上高を持つ次のマテリアライズド・ビューsum_sales_per_year_month_mvを考えてみます。

CREATE MATERIALIZED VIEW  sum_sales_per_year_month_mv
BUILD IMMEDIATE
REFRESH FORCE ON DEMAND
ENABLE QUERY REWRITE AS
SELECT s.calendar_year, s.calendar_month_number,
SUM(s.amount_sold) AS sum_sales, COUNT(*) AS cnt
FROM sales_par_range_list s WHERE s.calendar_year > 1990
GROUP BY s.calendar_year, s.calendar_month_number;

sales_per_country_mvは、レンジ・パーティション化レベルでsales_par_range_listに対するPCTをサポートします。これは、レンジ・パーティション・キーcalendar_month_numberが、SELECTおよびGROUP BYリストにあるためです。

INSERT INTO sales_par_range_list
VALUES (2001, 3, 25, 'Spain', 20, 'PROD20',  300,  20.50);

この文では、calendar_month_number = 3およびcountry_name = 'Spain'を含む行を挿入します。この行は、パーティションq1、サブパーティションEuropeに挿入されます。このINSERT文の後、sales_par_range_listのパーティションq1に関して、sum_sales_per_year_month_mvは失効します。したがって、次の文のように、sales_par_range_listのこのパーティションのデータにアクセスする入力問合せは、すべてリライトできません。

次の問合せでは、パーティションq1およびq2のデータにアクセスします。q1が更新され、マテリアライズド・ビューはq1に関して失効しているため、PCTリライトは使用できません。

SELECT s.calendar_year, SUM(s.amount_sold) AS sum_sales, COUNT(*) AS cnt
FROM sales_par_range_list s
WHERE s.calendar_year = 2000
AND s.calendar_month_number BETWEEN 2 AND 6
GROUP BY s.calendar_year;

次に、INSERT文の後にリライトを行う文の例を示します。これは最新のデータにアクセスするためです。

SELECT s.calendar_year, SUM(s.amount_sold) AS sum_sales, COUNT(*) AS cnt
FROM sales_par_range_list s
WHERE s.calendar_year = 2000 AND s.calendar_month_number BETWEEN 5 AND 9
GROUP BY s.calendar_year;

図19-3に、失効した部分と最新の部分を示します。

図19-3 PCTリライトとレンジ-リスト・パーティション化

リスト・パーティション表に基づいたPCTリライト

マテリアライズド・ビューのSELECTおよびGROUP BYにLISTパーティション化キーがある場合、PCTはマテリアライズド・ビューでサポートされます。サポートされているパーティション化のタイプに関係なく、ディテール表のパーティション・マーカーまたはROWIDがマテリアライズド・ビューにある場合、PCTは該当する特定のディテール表のマテリアライズド・ビューでサポートされます。

CREATE TABLE sales_par_list
(calendar_year, calendar_month_number, day_number_in_month,
country_name, prod_id, quantity_sold, amount_sold)
PARTITION BY LIST (country_name)
(PARTITION America
VALUES ('United States of America', 'Argentina'),
PARTITION Asia
VALUES ('Japan', 'India'),
PARTITION Europe
VALUES ('France', 'Spain', 'Ireland'))
AS SELECT t.calendar_year, t.calendar_month_number,
t.day_number_in_month, c1.country_name, s.prod_id,
s.quantity_sold, s.amount_sold
FROM times t, countries c1, sales s, customers c2
WHERE s.time_id = t.time_id and s.cust_id = c2.cust_id and
c2.country_id = c1.country_id and
c1.country_name IN ('United States of America', 'Argentina',
'Japan', 'India', 'France', 'Spain', 'Ireland');

マテリアライズド・ビューが表sales_par_listで作成され、その中にリスト・パーティション化キーがある場合、PCTリライトでは、このマテリアライズド・ビューをリライト対象として使用します。

この機能を理解するために、毎年の各国における全製品の合計売上高を持つマテリアライズド・ビューの作成例を次に示します。ビューは、ディテール表sales_par_listおよびproductsに依存しています。

CREATE MATERIALIZED VIEW sales_per_country_mv
BUILD IMMEDIATE
REFRESH FORCE ON DEMAND
ENABLE QUERY REWRITE AS
SELECT s.calendar_year AS calendar_year, s.country_name AS country_name,
p.prod_name AS prod_name, SUM(s.amount_sold) AS sum_sales, COUNT(*) AS cnt
FROM sales_par_list s, products p
WHERE s.prod_id = p.prod_id AND s.calendar_year <= 2000
GROUP BY s.calendar_year, s.country_name, prod_name;

sales_per_country_mvは、sales_par_listに対するPCTをサポートします。これは、リスト・パーティション・キーcountry_nameが、SELECTおよびGROUP BYリストにあるためです。表productsはパーティション化されません。したがってsales_per_country_mvはこの表に対するPCTをサポートしていません。

入力問合せがマテリアライズド・ビューの最新の部分にのみアクセスする場合に、sales_per_country_mvが失効していても、問合せは、(ENFORCEDモードまたはTRUSTEDモードで)sales_per_country_mvに関して、リライトできます。FRESHであるマテリアライズド・ビューの部分を判別できるのは、更新された表がマテリアライズド・ビューでPCTを使用できる場合のみです。したがって、PCTを使用できない表が更新された場合、マテリアライズド・ビューのFRESHの部分を判別できないため、特定のマテリアライズド・ビューの最新データによるリライトはできません。

sales_per_country_mvは、sales_par_listのPCTはサポートしますが、表productのPCTはサポートしません。表productsが更新されても、マテリアライズド・ビューのどの部分がFRESHであるかを識別できないため、sales_per_country_mvでのPCTリライトはできません。

次の文は、sales_par_listを更新します。

INSERT INTO sales_par_list VALUES (2000, 10, 22, 'France', 900, 20, 200.99);

この文では、表sales_par_list内のパーティションEuropeに行を挿入しています。この結果、sales_per_country_mvは失効しますが、このマテリアライズド・ビューは表sales_par_listに対するPCTをサポートするため、(ENFORCEDモードおよびTRUSTEDモードでの)PCTリライトが可能となります。マテリアライズド・ビューの最新の部分および失効した部分は、パーティション化されたディテール表sales_par_listに基づいて識別されます。

図19-4に、この例における最新の部分と失効した部分を示します。

図19-4 PCTリライトとリスト・パーティション化

次の問合せを考えてみます。

SELECT s.country_name, p.prod_name, SUM(s.amount_sold) AS sum_sales,
COUNT(*) AS cnt
FROM sales_par_list s, products p
WHERE s.prod_id = p.prod_id AND s.calendar_year = 2000
AND s.country_name IN ('United States of America', 'Japan')
GROUP BY s.country_name, p.prod_name;

この問合せでは、sales_par_listのパーティションAmericaおよびAsiaにアクセスします。これらのパーティションは未更新であり、この問合せのアクセス対象はマテリアライズド・ビューのFRESHの部分に限定されるため、失効したマテリアライズド・ビューsales_per_country_mvでリライトできます。

問合せは、次のように、sales_per_country_mvでリライトされます。

SELECT country_name, prod_name, SUM(sum_sales) AS sum_slaes, SUM(cnt) AS cnt
FROM sales_per_country_mv WHERE calendar_year = 2000
AND country_name IN ('United States of America', 'Japan')
GROUP BY country_name, prod_name;

次の問合せを考えてみます。

SELECT s.country_name,  p.prod_name,
SUM(s.amount_sold) AS sum_sales, COUNT(*) AS cnt
FROM sales_par_list s, products p
WHERE  s.prod_id = p.prod_id AND s.calendar_year = 1999
AND s.country_name IN ('Japan', 'India', 'Spain')
GROUP BY s.country_name, p.prod_name;

この問合せでは、sales_par_listのパーティションEuropeおよびAsiaにアクセスします。パーティションEuropeは更新されていますが、マテリアライズド・ビューの必要なデータが失効しているため、この問合せはsales_per_country_mvでリライトできません。

入力問合せがマテリアライズド・ビューのFRESHの部分にアクセスする場合、DML、ダイレクト・ロード、パーティション・メンテナンス操作(PMOP)など、sales_par_listに対する任意の種類の更新後に、リライトできるようになります。

PCTリライトとPMARKER

パーティション・マーカーを指定して、特定のパーティションのすべての行が同じpmarker値を持つと、クエリー・リライト機能が、ディテール表の全パーティションにアクセスする問合せのリライトに制限されます。つまり、ディテール表のパーティションの一部にアクセスする問合せの場合、マテリアライズド・ビューのFRESHの部分にデータが対応していても、リライトは行われません。この場合、マテリアライズド・ビューのFRESHの部分は、pmarker値によって決定されます。マテリアライズド・ビューの最新の行を決定するには、データの新しさとマーカー値を関連付けます。これにより、特定のpmarker値を持つマテリアライズド・ビューのすべての行が、FRESHまたはSTALEになります。

次に、各月について、sales_par_listの各ディテール表のパーティションにおける、全製品の合計売上高を表すマテリアライズド・ビューを作成する例を示します。次に示すように、このマテリアライズド・ビューはディテール表productsにも依存します。

CREATE MATERIALIZED VIEW sales_per_dt_partition_mv
BUILD IMMEDIATE
REFRESH FORCE ON DEMAND
ENABLE QUERY REWRITE AS
SELECT s.calendar_year AS calendar_year, p.prod_name AS prod_name,
DBMS_MVIEW.PMARKER(s.rowid) pmarker,
SUM(s.amount_sold) AS sum_sales, COUNT(*) AS cnt
FROM sales_par_list s, products p
WHERE s.prod_id = p.prod_id AND s.calendar_year > 2000
GROUP BY s.calendar_year, DBMS_MVIEW.PMARKER(s.rowid), p.prod_name;

マテリアライズド・ビューsales_per_dt_partition_mvでは、ディテール表のパーティションごとの合計売上高を提供します。このマテリアライズド・ビューは、パーティション・マーカーがSELECT句およびGROUP BY句にあるため、表sales_per_listに対するPCTリライトをサポートします。表19-2に、この例でのパーティションの名前およびpmarkerを示します。

表19-2 パーティションの名前およびPmarker

パーティションの名前

Pmarker

America

1000

Asia

1001

Europe

1002

次のように、sales_par_listを更新します。

DELETE FROM sales_par_list WHERE country_name = 'India';

これで、表sales_par_listのパーティションAsiaから行が削除されました。この結果、sales_per_dt_partition_mvは失効しますが、このマテリアライズド・ビューは表sales_par_listに対するPCT(pmarkerベース)をサポートするため、(ENFORCEDモードおよびTRUSTEDモードでの)PCTリライトが可能となります。

次の問合せを考えてみます。

SELECT p.prod_name, SUM(s.amount_sold) AS sum_sales, COUNT(*) AS cnt
FROM sales_par_list s, products p
WHERE  s.prod_id = p.prod_id AND s.calendar_year = 2001 AND
s.country_name IN ('United States of America', 'Argentina')
GROUP BY p.prod_name;

ディテール表のパーティションに対応するすべてのデータがアクセスされ、このデータに関してマテリアライズド・ビューはFRESHであるため、この問合せはsales_per_dt_partition_mvでリライトできます。この問合せは、未更新のパーティションAmericaのすべてのデータにアクセスします。

問合せは、次のように、sales_per_dt_partition_mvでリライトされます。

SELECT prod_name, SUM(sum_sales) AS sum_sales, SUM(cnt) AS cnt
FROM sales_per_dt_partition_mv
WHERE calendar_year = 2001 AND pmarker = 1000
GROUP BY prod_name;

PMARKERとしてROWIDを使用したPCTリライト

マテリアライズド・ビューは、GROUP BY句があり、SELECTおよびGROUP BY句にパーティション・キーまたはパーティション・マーカーが指定されている場合、PCTリライトをサポートします。pmarkerまたはパーティション・キーのかわりに、パーティション表のROWIDを使用できます。この場合、内部的にROWIDがpmarkerに変換されます。次の表を考えてみます。

CREATE TABLE product_par_list
(prod_id, prod_name, prod_category,
prod_subcategory, prod_list_price)
PARTITION BY LIST (prod_category)
(PARTITION prod_cat1
VALUES ('Boys', 'Men'),
PARTITION prod_cat2
VALUES ('Girls', 'Women'))
AS
SELECT prod_id, prod_name, prod_category,
prod_subcategory, prod_list_price
FROM products;

表sales_par_listおよびproduct_par_listのマテリアライズド・ビューを作成する例を次に示します。

CREATE MATERIALIZED VIEW  sum_sales_per_category_mv
BUILD IMMEDIATE
REFRESH FORCE ON DEMAND
ENABLE QUERY REWRITE AS
SELECT p.rowid prid, p.prod_category,
SUM (s.amount_sold) sum_sales, COUNT(*) cnt
FROM sales_par_list s, product_par_list p
WHERE s.prod_id = p.prod_id and s.calendar_year <= 2000
GROUP BY p.rowid, p.prod_category;

pmarkerでのリライトに適用されるすべての制限が、ここでも適用されます。入力問合せは、リライト対象の問合せの全パーティションにアクセスする必要があります。この場合に使用されるpmarker表を次に示します。

product_par_list       pmarker value
----------------       -------------
prod_cat1                      1000
prod_cat2                      1001
prod_cat3                      1002

次のように、product_par_listを更新します。

DELETE FROM product_par_list WHERE prod_name = 'MEN';

この結果、product_par_listのパーティションprod_list1に関して、sum_sales_per_category_mvは失効します。

次の問合せを考えてみます。

SELECT p.prod_category, SUM(s.amount_sold) AS sum_sales, COUNT(*) AS cnt
FROM sales_par_list s, product_par_list p
WHERE  s.prod_id = p.prod_id AND p.prod_category IN
('Girls', 'Women') AND s.calendar_year <= 2000
GROUP BY p.prod_category;

ディテール表のパーティションに対応するすべてのデータがアクセスされ、このデータに関してマテリアライズド・ビューはFRESHであるため、この問合せはsum_sales_per_category_mvでリライトできます。この問合せは、未更新のパーティションprod_cat2のすべてのデータにアクセスします。次に、sum_sales_per_category_mvでリライトされた問合せを示します。

SELECT prod_category, sum_sales, cnt
FROM sum_sales_per_category_mv WHERE DBMS_MVIEW.PMARKER(srid) IN (1000)
GROUP BY prod_category;

複数のマテリアライズド・ビュー

クエリー・リライトは、複数のマテリアライズド・ビューを使用した問合せのリライトができるように拡張されました。クエリー・リライトにおいて、すべてのデータを戻すマテリアライズド・ビューのセットがないと判断された場合、残りのデータは実表から取得されます。

複数のマテリアライズド・ビューを使用したクエリー・リライトでは、PCTやINリストを使用したリライトなど、様々なタイプのリライトを活用できる他、それらを組み合せて使用することもできます。以下では、現在クエリー・リライトが可能な問合せについて、具体例をいくつか取り上げます。

2つのマテリアライズド・ビュー、cust_avg_credit_mv1とcust_avg_credit_mv2について考えてみます。cust_avg_credit_mv1では、各郵便番号について、1940年から1950年に生まれたすべての顧客の平均与信限度額を問い合せます。cust_avg_credit_mv2では、各郵便番号について、1951年から1970年に生まれた顧客の平均与信限度額を問い合せます。

この例で定義されるマテリアライズド・ビューを次に示します。

CREATE MATERIALIZED VIEW cust_avg_credit_mv1
ENABLE QUERY REWRITE
AS SELECT cust_postal_code, cust_year_of_birth,
SUM(cust_credit_limit) AS sum_credit,
COUNT(cust_credit_limit) AS count_credit
FROM customers
WHERE cust_year_of_birth BETWEEN 1940 AND 1950
GROUP BY cust_postal_code, cust_year_of_birth;

CREATE MATERIALIZED VIEW cust_avg_credit_mv2
ENABLE QUERY REWRITE
AS SELECT cust_postal_code, cust_year_of_birth,
SUM(cust_credit_limit) AS sum_credit,
COUNT(cust_credit_limit) AS count_credit
FROM customers
WHERE cust_year_of_birth > 1950 AND cust_year_of_birth <= 1970
GROUP BY cust_postal_code, cust_year_of_birth;

問合せ1: マテリアライズド・ビューと問合せにおける1つの範囲の一致

各郵便番号について、1940年から1970年に生まれたすべての顧客の平均与信限度額を問い合せる問合せを考えてみます。この問合せは、cust_year_of_birthに対してBETWEENで指定した範囲と一致します。

SELECT cust_postal_code, AVG(cust_credit_limit) AS avg_credit
FROM customers c
WHERE cust_year_of_birth BETWEEN 1940 AND 1970
GROUP BY cust_postal_code;

前述の問合せは、次のように、すべてのデータを取得するために、2つのマテリアライズド・ビューでリライトできます。

SELECT v1.cust_postal_code,
SUM(v1.sum_credit)/SUM(v1.count_credit) AS avg_credit
FROM (SELECT cust_postal_code, sum_credit, count_credit
FROM cust_avg_credit_mv1
GROUP BY cust_postal_code
UNION ALL
SELECT cust_postal_code, sum_credit, count_credit
FROM cust_avg_credit_mv2
GROUP BY cust_postal_code) v1
GROUP BY v1.cust_postal_code;

インライン・ビューにUNION ALLの問合せが使用されているのは、再集計の必要があるためです。また、クエリー・リライトがどのように集計をカウントしてこのロールアップを実行したかを確認してください。

問合せ2: マテリアライズド・ビューに含まれるデータ外の問合せ

マテリアライズド・ビューで指定している範囲が、問合せで問い合せる範囲を超える場合、リライトされる問合せにはフィルタ(選択述語)が追加され、マテリアライズド・ビューによって戻される不要な行が削除されます。これは、次のような問合せの場合です。

SELECT cust_postal_code, SUM(cust_credit_limit) AS sum_credit
FROM customers c
WHERE cust_year_of_birth BETWEEN 1945 AND 1955
GROUP BY cust_postal_code;

問合せ2は、次のようにリライトされます。

SELECT v1.cust_postal_code, SUM(v1.sum_credit)
FROM
(SELECT cust_postal_code, SUM(sum_credit) AS sum_credit
FROM cust_avg_credit_mv1
WHERE cust_year_of_birth BETWEEN 1945 AND 1950
GROUP BY cust_postal_code
UNION ALL
SELECT cust_postal_code, SUM(sum_credit) AS sum_credit
FROM cust_birth_mv2
WHERE cust_year_of_birth > 1950 AND cust_year_of_birth <= 1955
GROUP BY cust_postal_code) v1
GROUP BY v1.cust_postal_code;

問合せ3: マテリアライズド・ビューよりも多くのデータの問合せ

問合せで、前述の2つのマテリアライズド・ビューに含まれているデータよりも多くのデータを問い合せる場合を考えます。その場合でも、両方のマテリアライズド・ビューおよび実表のデータを使用してリライトされます。次の例では、集計がないマテリアライズド・ビューの新しいセットを定義し、両方のマテリアライズド・ビューおよび実表のデータを使用してリライトを行います。

CREATE MATERIALIZED VIEW cust_birth_mv1
ENABLE QUERY REWRITE
AS SELECT cust_last_name, cust_first_name, cust_year_of_birth
FROM customers WHERE cust_year_of_birth BETWEEN 1940 AND 1950;

CREATE MATERIALIZED VIEW cust_avg_credit_mv2
ENABLE QUERY REWRITE
AS SELECT cust_last_name, cust_first_name, cust_year_of_birth
FROM customers
WHERE cust_year_of_birth > 1950 AND cust_year_of_birth <= 1970;

この問合せでは、1940年から1990年に生まれたすべての顧客を必要としています。

SELECT cust_last_name, cust_first_name
FROM customers c WHERE cust_year_of_birth BETWEEN 1940 AND 1990;

クエリー・リライトは、1971年から1990年に生まれた顧客にアクセスするため、実表にアクセスする必要があります。したがって、問合せ3は次のようにリライトされます。

SELECT cust_last_name, cust_first_name
FROM cust_birth_mv1
UNION ALL
SELECT cust_last_name, cust_first_name
FROM cust_birth_mv2
UNION ALL
SELECT cust_last_name, cust_first_name
FROM customers c
WHERE cust_year_of_birth > 1970 AND cust_year_of_birth <= 1990;

問合せ4: 複数の選択列のデータの問合せ

次に、1945年から1960年に生まれ、与信限度額が1,000から10,000のすべての顧客を問い合せる問合せを考えてみます。これは、複数の選択列のデータを問い合せるため、複数選択の問合せです。

SELECT cust_last_name, cust_first_name
FROM customers WHERE cust_year_of_birth BETWEEN 1945 AND 1960 AND
cust_credit_limit BETWEEN 1000 AND 10000;

図19-5は、2つの選択問合せを示します。これらは、次の項で説明する2つの選択マテリアライズド・ビューでリライトできます。

図19-5 複数のマテリアライズド・ビューを使用したクエリー・リライト

図19-5のグラフは、この問合せを満たすために使用できるマテリアライズド・ビューを表したものです。credit_mv1は、1945から1950年生まれで、与信限度額が1,000から5,000の顧客を問い合せます。credit_mv2は、1945から1960年生まれで、与信限度額が5,000より多く、10,000以下の顧客を問い合せます。credit_mv3は、1951から1955年生まれで、与信限度額が1,000から5,000の顧客を問い合せます。

この場合のマテリアライズド・ビューの定義を次に示します。

CREATE MATERIALIZED VIEW credit_mv1
ENABLE QUERY REWRITE
AS SELECT cust_last_name, cust_first_name,
cust_credit_limit, cust_year_of_birth
FROM customers
WHERE cust_credit_limit BETWEEN 1000 AND 5000
AND cust_year_of_birth BETWEEN 1945 AND 1950;

CREATE MATERIALIZED VIEW credit_mv2
ENABLE QUERY REWRITE
AS SELECT cust_last_name, cust_first_name,
cust_credit_limit, cust_year_of_birth
FROM customers
WHERE cust_credit_limit > 5000
AND cust_credit_limit <= 10000 AND cust_year_of_birth
BETWEEN 1945 AND 1960;

CREATE MATERIALIZED VIEW credit_mv3
ENABLE QUERY REWRITE AS
SELECT cust_last_name, cust_first_name,
cust_credit_limit, cust_year_of_birth
FROM customers
WHERE cust_credit_limit BETWEEN 1000 AND 5000
AND cust_year_of_birth > 1950 AND cust_year_of_birth <= 1955;

問合せ4は、ほとんどのデータにアクセスするこれら3つのマテリアライズド・ビューを使用してリライトできます。ただし、一部のデータはこれら3つのマテリアライズド・ビューからは取得できないため、クエリー・リライトは実表にもアクセスして、1955年から1960年生まれで、与信限度額が1,000から5,000である顧客のデータを取得します。次のようにリライトされます。

SELECT cust_last_name, cust_first_name
FROM credit_mv1
UNION ALL
SELECT cust_last_name, cust_first_name
FROM credit_mv2
UNION ALL
SELECT cust_last_name, cust_first_name
FROM credit_mv3
UNION ALL
SELECT cust_last_name, cust_first_name
FROM customers
WHERE cust_credit_limit BETWEEN 1000 AND 5000
AND cust_year_of_birth > 1955 AND cust_year_of_birth <= 1960;

この例では、複数のマテリアライズド・ビューを使用して複数選択の問合せをリライトする方法を示しています。この例は、3つのマテリアライズド・ビュー間でデータが重複しないように単純化されています。これに対し、クエリー・リライトでは同様のリライトを実行できます。

問合せ5: 範囲および制約範囲

この例では、単一選択マテリアライズド・ビューを使用して複数選択の問合せをリライトする方法を示します。この例では、問合せに2つの範囲指定があり、マテリアライズド・ビューには1つの制約範囲指定があります。問合せでは、1945年から1960年生まれで、与信限度額が1,000から10,000の顧客を問い合せます。一方、credit_mv1では与信限度額が1,000から5,000の顧客のみを問い合せるとします。credit_mv1はcust_year_of_birthの選択述語で制約されていないので、問合せの出生年値の全範囲をカバーしています。

図19-6 制約されたマテリアライズド・ビューの選択述語

図19-6では、下部の領域がcredit_mv1のデータを表しています。

新しいcredit_mv1は、次のように定義されます。

CREATE MATERIALIZED VIEW credit_mv1
ENABLE QUERY REWRITE
AS SELECT cust_last_name, cust_first_name,
cust_credit_limit, cust_year_of_birth
FROM customers WHERE cust_credit_limit BETWEEN 1000 AND 5000;

問合せは次のとおりです。

SELECT cust_last_name, cust_first_name
FROM customers WHERE cust_year_of_birth BETWEEN 1945 AND 1960
AND cust_credit_limit BETWEEN 1000 AND 10000;

最終的にリライトされた問合せは、次のようになります。

SELECT cust_last_name, cust_first_name
FROM credit_mv1 WHERE cust_year_of_birth BETWEEN 1945 AND 1960
UNION ALL
SELECT cust_last_name, cust_first_name
FROM customers WHERE cust_year_of_brith BETWEEN 1945 AND 1960
AND cust_credit_limit > 5000 AND cust_credit_limit <= 10000;

問合せ6: 単一列のINリストが指定された問合せと単一列の範囲が指定されたマテリアライズド・ビュー

複数のマテリアライズド・ビューのクエリー・リライトでは、入力問合せのINリストを処理し、同じ選択列の範囲指定を持つマテリアライズド・ビューで問合せをリライトできます。前述の範囲指定のみの例に対する自然な流れとして、INリストでは範囲内の離散値を表すものとします。

次の例では、1つの列のINリストの選択述語を持つ問合せと、1つの列の範囲選択述語を持つマテリアライズド・ビューを示します。1945、1950、1955、1960、1965、1970または1975年に生まれた、各国の顧客数を問い合せる問合せを考えてみます。この問合せは、cust_year_of_birthのINリストで制約されます。

SELECT c2.country_name, count(c1.country_id)
FROM customers c1, countries c2
WHERE c1.country_id = c2.country_id AND
c1.cust_year_of_birth IN (1945, 1950, 1955, 1960, 1965, 1970, 1975)
GROUP BY c2.country_name;

次の2つのマテリアライズド・ビューを考えてみます。cust_country_birth_mv1では、1940年から1950年に生まれた、各国の顧客数を問い合せます。cust_country_birth_mv2では、1951年から1970年に生まれた、各国の顧客数を問い合せます。前述の問合せは、1945、1950、1955、1960、1965および1970年生まれの各国の顧客総数を取得するために、これら2つのマテリアライズド・ビューでリライトできます。1975年生まれの顧客数を取得するためには、実表にアクセスする必要があります。

この例で定義されるマテリアライズド・ビューを次に示します。

CREATE MATERIALIZED VIEW cust_country_birth_mv1
ENABLE QUERY REWRITE
AS SELECT c2.country_name, c1.cust_year_of_birth,
COUNT(c1.country_id) AS count_customers
FROM customers c1, countries c2
WHERE c1.country_id = c2.country_id AND
cust_year_of_birth BETWEEN 1940 AND 1950
GROUP BY c2.country_name, c1.cust_year_of_birth;

CREATE MATERIALIZED VIEW cust_country_birth_mv2
ENABLE QUERY REWRITE
AS SELECT c2.country_name, c1.cust_year_of_birth,
COUNT(c1.country_id) AS count_customers
FROM customers c1, countries c2
WHERE c1.country_id = c2.country_id AND cust_year_of_birth > 1950
AND cust_year_of_birth <= 1970
GROUP BY c2.country_name, c1.cust_year_of_birth;

したがって、問合せ6は次のようにリライトされます。

SELECT v1.country_name, SUM(v1.count_customers)
FROM (SELECT country_name, SUM(count_customers) AS count_customers
FROM cust_country_birth_mv1
WHERE cust_year_of_birth IN (1945, 1950)
GROUP BY country_name
UNION ALL
SELECT country_name, SUM(count_customers) AS count_customers
FROM cust_country_birth_mv2
WHERE cust_year_of_birth IN (1955, 1960, 1965, 1970)
GROUP BY country_name
UNION ALL
SELECT c2.country_name, COUNT(c1.country_id) AS count_customers
FROM customers c1, countries c2
WHERE c1.country_id = c2.country_id AND cust_year_of_birth IN (1975)
GROUP BY c2.country_name) v1
GROUP BY v1.country_name;

問合せ7: 複数のマテリアライズド・ビューを使用したPCTリライト

複数のマテリアライズド・ビューを使用したリライトでも、PCTリライトを利用できます。PCTリライトは、マテリアライズド・ビューの失効時にマテリアライズド・ビューの最新部分のみを使用して問合せをリライトする機能です。この機能は、ENFORCEDまたはTRUSTEDの整合性モード、および、複数のマテリアライズド・ビューによるリライトで使用され、最新データはマテリアライズド・ビューの最新部分から取得し、失効データは実表から取得できます。したがって、リライトされる問合せでは、1つ以上のマテリアライズド・ビューの最新部分のみに対してUNION ALLを行い、残りのデータを実表から取得して、結果を戻します。そのため、ここではPCTのすべての規則と条件も適用されます。マテリアライズド・ビューではPCTを有効化し、実表に対する変更は、マテリアライズド・ビューの最新部分と失効部分を明確に識別できるように実行する必要があります。

この例では、1945年から1964年生まれで、与信限度額が1,000から10,000の顧客を問い合せる問合せを考えます。また、顧客(customers)表はcust_date_of_birthでパーティション化されており、PCTを有効化したマテリアライズド・ビューcredit_mv1でも、1945年から1964年生まれで、与信限度額が1,000から10,000の顧客を問い合せるものとします。

SELECT cust_last_name, cust_first_name
FROM customers WHERE cust_credit_limit BETWEEN 1000 AND 10000;

図19-7では、実表のパーティションp1からp6に関して、マテリアライズド・ビューの最新部分および失効部分をそれぞれグレーと白で示しています。

図19-7 PCTリライトと複数のマテリアライズド・ビューによるリライト

ここでは、ENFORCEDモードで、顧客表のp1、p2、p3、p5およびp6が最新部分、パーティションp4が失効部分だとします。これは、問合せに回答するのにcredit_mv1のすべてのパーティションが使用できないことを意味します。リライトされる問合せでは、顧客パーティションp4の結果を他の特定のマテリアライズド・ビューから取得するか、この例で示すように、実表から取得する必要があります。次に、表がどのようにパーティション化されているのかがわかるcustomers表の表定義の一部を示します。

CREATE TABLE customers
(PARTITION BY RANGE (cust_year_of_birth)
PARTITION p1 VALUES LESS THAN (1945),
PARTITION p2 VALUES LESS THAN (1950),
PARTITION p3 VALUES LESS THAN (1955),
PARTITION p4 VALUES LESS THAN (1960),
PARTITION p5 VALUES LESS THAN (1965),
PARTITION p6 VALUES LESS THAN (1970);

次に、前述の例のマテリアライズド・ビューの定義を示します。

CREATE MATERIALIZED VIEW credit_mv1
ENABLE QUERY REWRITE
AS SELECT cust_last_name, cust_first_name,
cust_credit_limit, cust_year_of_birth
FROM customers
WHERE cust_credit_limit BETWEEN 1000 AND 10000
AND cust_year_of_birth BETWEEN 1945 AND 1964;

このマテリアライズド・ビューでは、表customersに関してPCTが有効化されています。

リライトされた問合せは、次のようになります。

SELECT cust_last_name, cust_first_name FROM credit_mv1
WHERE cust_credit_limit BETWEEN 1000 AND 10000 AND
(cust_year_of_birth >= 1945 AND cust_year_of_birth < 1955 OR
cust_year_of_birth BETWEEN 1945 AND 1964)
UNION ALL
SELECT cust_last_name, cust_first_name
FROM customers WHERE cust_credit_limit BETWEEN 1000 AND 10000
AND cust_year_of_birth < 1960 AND cust_year_of_birth >= 1955;

その他のクエリー・リライトの考慮事項

ここでは、クエリー・リライトが可能なその他のケースについて説明します。

- ネステッド・マテリアライズド・ビューを使用したクエリー・リライト

- インライン・ビューがある場合のクエリー・リライト

- リモート表を使用したクエリー・リライト

- 表複製がある場合のクエリー・リライト

- デート・フォールディングを使用したクエリー・リライト

- ビューの制約を使用したクエリー・リライト

- 集合演算子を含むマテリアライズド・ビューを使用したクエリー・リライト

- グルーピング・セットがある場合のクエリー・リライト

- ウィンドウ関数がある場合のクエリー・リライト

- クエリー・リライトおよび式の一致

- カーソルの共有とバインド変数

- クエリー・リライトでの式の処理

ネステッド・マテリアライズド・ビューを使用したクエリー・リライト

クエリー・リライトでは、ネステッド・マテリアライズド・ビューが繰り返し利用されます。Oracle Databaseでは、まず、集計および結合を持つマテリアライズド・ビューを使用して、クエリー・リライトを試みます。次に、結合のみを含むマテリアライズド・ビューを使用して試みます。いずれかのリライトが成功した場合、Oracleはリライトがなくなるまで、そのプロセスを繰り返します。たとえば、次のようにマテリアライズド・ビューjoin_sales_time_product_mvとsum_sales_time_product_mvを作成したとします。

CREATE MATERIALIZED VIEW join_sales_time_product_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_id, p.prod_name, t.time_id, t.week_ending_day,
s.channel_id, s.promo_id, s.cust_id, s.amount_sold
FROM   sales s, products p, times t
WHERE  s.time_id=t.time_id AND s.prod_id = p.prod_id;

CREATE MATERIALIZED VIEW sum_sales_time_product_mv
ENABLE QUERY REWRITE AS
SELECT mv.prod_name, mv.week_ending_day, COUNT(*) cnt_all,
SUM(mv.amount_sold) sum_amount_sold,
COUNT(mv.amount_sold) cnt_amount_sold
FROM join_sales_time_product_mv mv
GROUP BY mv.prod_name, mv.week_ending_day;

次の問合せを考えてみます。

SELECT p.prod_name, t.week_ending_day, SUM(s.amount_sold)
FROM sales s, products p, times t
WHERE s.time_id=t.time_id AND s.prod_id=p.prod_id
GROUP BY p.prod_name, t.week_ending_day;

Oracleは、join_sales_time_product_mvがリライトに適していると判断します。リライトされた問合せの形式は、次のとおりです。

SELECT mv.prod_name, mv.week_ending_day, SUM(mv.amount_sold)
FROM join_sales_time_product_mv mv
GROUP BY mv.prod_name, mv.week_ending_day;

リライトが発生したため、Oracleはこのプロセスを再度試みます。ここでは、前述の問合せは、単一表集計マテリアライズド・ビューsum_sales_store_timeを使用して、次の形式にリライトされます。

SELECT mv.prod_name, mv.week_ending_day, mv.sum_amount_sold
FROM sum_sales_time_product_mv mv;

インライン・ビューがある場合のクエリー・リライト

Oracle Databaseでは、次の2つの場合にインライン・ビューによるクエリー・リライトをサポートしています。

- マテリアライズド・ビューに含まれるインライン・ビューのテキストが問合せのテキストと完全に一致している場合

- 問合せの中に含まれるインライン・ビューが、マテリアライズド・ビューに含まれるインライン・ビューと同等である場合

2つのインライン・ビューが同等であるとみなされるのは、それぞれのSELECT構文のリストおよびGROUP BYリストが同等であり、FROM句に同一または同等のオブジェクトが含まれ、WHERE句の中のすべての選択述語を含む結合グラフが同等であり、さらにHAVING句が同等である場合です。

次の例は、インライン・ビューを持つ問合せに対し、テキスト一致リライトおよび一般的なインライン・ビュー・リライトを使用したマテリアライズド・ビューによるリライトがどのように行われるかを示します。インライン・ビューを含む次のマテリアライズド・ビューを考えてみます。

CREATE MATERIALIZED VIEW SUM_SALES_MV
ENABLE QUERY REWRITE AS
SELECT mv_iv.prod_id, mv_iv.cust_id,
sum(mv_iv.amount_sold) sum_amount_sold
FROM (SELECT prod_id, cust_id, amount_sold
FROM sales, products
WHERE sales.prod_id = products.prod_id) MV_IV
GROUP BY mv_iv.prod_id, mv_iv.cust_id;

次の問合せに含まれるインライン・ビューのテキストは、マテリアライズド・ビューのインライン・ビューのテキストと完全に一致しています。そのためこの問合せでは、リライトが行えるように、インライン・ビューが内部的にマテリアライズド・ビューのインライン・ビューと置き換えられます。

SELECT iv.prod_id, iv.cust_id,
SUM(iv.amount_sold) sum_amount_sold
FROM (SELECT prod_id, cust_id, amount_sold
FROM sales, products
WHERE sales.prod_id = products.prod_id) IV
GROUP BY iv.prod_id, iv.cust_id;

次の問合せに含まれるインライン・ビューのテキストは、前述のマテリアライズド・ビューに含まれるインライン・ビューのテキストと完全には一致していません。問合せのインライン・ビューの中の結合述語が置き換えられていることに注意してください。この問合せのテキストは、マテリアライズド・ビューに含まれるインライン・ビューのテキストに一致していませんが、クエリー・リライトでは、この問合せのインライン・ビューが、マテリアライズド・ビューに含まれるインライン・ビューと同等であると判断されます。ここでも同様に、この問合せに対してリライトが行えるよう、問合せのインライン・ビューが内部的にマテリアライズド・ビューのインライン・ビューと置き換えられます。

SELECT iv.prod_id, iv.cust_id,
SUM(iv.amount_sold) sum_amount_sold
FROM (SELECT prod_id, cust_id, amount_sold
FROM sales, products
WHERE products.prod_id = sales.prod_id) IV
GROUP BY iv.prod_id, iv.cust_id;

これらの問合せはいずれも、次のようにSUM_SALES_MVでリライトされます。

SELECT prod_id, cust_id, sum_amount_sold
FROM SUM_SALES_MV;

集合演算子、GROUPING SET句、ネストした副問合せ、ネストしたインライン・ビュー、およびリモート表を含む問合せでは、一般的なインライン・ビュー・リライトはサポートされていません。

リモート表を使用したクエリー・リライト

Oracle Databaseは、単一のリモート・データベース・サイトの表を参照するマテリアライズド・ビューでのクエリー・リライトをサポートしています。ただしマテリアライズド・ビューは、問合せの発行が行われているサイトに存在する必要があります。リモート表の更新内容は、即座にはローカル・サイトに伝播されないため、クエリー・リライトはstale_toleratedモードでのみ実行されます。マテリアライズド・ビューにはない列が含まれている問合せでは、後戻り結合という手法を使用してクエリー・リライトが行われます。ただし、後戻り結合表がローカル・サイトにない場合は、クエリー・リライトは実行されません。また、リモート表の制約情報はリモート・サイトでは使用できないため、クエリー・リライトでは制約情報は使用されません。

次の問合せには、単一のリモート・サイトにある表が含まれています。

SELECT p.prod_id, t.week_ending_day, s.cust_id,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales@remotedbl s, products@remotedbl p, times@remotedbl t
WHERE s.time_id=t.time_id AND s.prod_id=p.prod_id
GROUP BY p.prod_id, t.week_ending_day, s.cust_id;

次のマテリアライズド・ビューはローカル・サイトにありますが、参照する表はすべてリモート・サイトにあります。

CREATE MATERIALIZED VIEW sum_sales_prod_week_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_id, t.week_ending_day, s.cust_id,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales@remotedbl s, products@remotedbl p, times@remotedbl t
WHERE s.time_id=t.time_id AND s.prod_id=p.prod_id
GROUP BY p.prod_id, t.week_ending_day, s.cust_id;

この問合せはリモート表を参照しますが、そのリライトは次のように前述のマテリアライズド・ビューを使用して行われます。

SELECT prod_id, week_ending_day, cust_id,  sum_amount_sold
FROM sum_sales_prod_week_mv;

表複製がある場合のクエリー・リライト

同じ表に対する複数の参照または自己結合を含む問合せのクエリー・リライトを行うには、2つの異なる方法があります。第1の方法では、問合せとマテリアライズド・ビューの定義で、表に対する複数の参照の別名が同じであるようにする必要があります。別名が異なる場合は、Oracleは第2の方法を試みます。この方法では、問合せの複数の参照がマテリアライズド・ビューの複数の参照に一致するように、問合せの結合とマテリアライズド・ビューの結合が比較されます。

マテリアライズド・ビューと問合せの例を次に示します。この例では、問合せには表の列の参照がないため、テキストの完全一致は機能しません。ただし、表の参照の別名は一致するため、一般的なクエリー・リライトは可能です。

shサンプル・スキーマで自己結合のリライトの可能性を示すために、ファクト表にある実際の出荷日と支払日を含むように、次のような追加が行われ、同じディメンション表timesを参照しているとします。これはあくまでも例であり、結果は戻されません。

ALTER TABLE sales ADD (time_id_ship DATE);
ALTER TABLE sales ADD (CONSTRAINT time_id_book_fk FOREIGN key (time_id_ship)
REFERENCES times(time_id) ENABLE NOVALIDATE);
ALTER TABLE sales MODIFY CONSTRAINT time_id_book_fk RELY;
ALTER TABLE sales ADD (time_id_paid DATE);
ALTER TABLE sales ADD (CONSTRAINT time_id_paid_fk FOREIGN KEY (time_id_paid)
REFERENCES times(time_id) ENABLE NOVALIDATE);
ALTER TABLE sales MODIFY CONSTRAINT time_id_paid_fk RELY;

これで、マテリアライズド・ビューを次のように定義できます。

CREATE MATERIALIZED VIEW sales_shipping_lag_mv
ENABLE QUERY REWRITE AS
SELECT t1.fiscal_week_number, s.prod_id,
t2.fiscal_week_number - t1.fiscal_week_number AS lag
FROM times t1, sales s, times t2
WHERE t1.time_id = s.time_id AND t2.time_id = s.time_id_ship;

次の問合せはテキストの完全一致テストにはパスしませんが、表の別名が一致するためリライトされます。

SELECT s.prod_id, t2.fiscal_week_number - t1.fiscal_week_number AS lag
FROM times t1, sales s, times t2
WHERE t1.time_id = s.time_id AND t2.time_id = s.time_id_ship;

Oracle Databaseでは、問合せ内で複数インスタンス化された表のインスタンスが、マテリアライズド・ビュー内の対応する表のインスタンスと正確に一致することを保証するために、他のチェックを実行することに注意してください。たとえば、次の例では、表timesの複数インスタンスで使用される別名は、マテリアライズド・ビューの表timesの複数インスタンスと一致しないと判断されます。

次の問合せでは、複数インスタンス化された表timeの別名は一致しますが、t2で別名化されたtimeのインスタンス間の結合条件とは互換性がないため、sales_shipping_lag_mvを使用してリライトできません。

SELECT s.prod_id, t2.fiscal_week_number - t1.fiscal_week_number AS lag
FROM times t1, sales s, times t2
WHERE t1.time_id = s.time_id AND t2.time_id = s.time_id_paid;

この問合せは、t2で別名化されたtime表のインスタンスをs.time_id_paid列で結合しますが、マテリアライズド・ビューは、t2で別名化されたtime表のインスタンスをs.time_id_ship列で結合します。結合条件が異なるため、Oracleではリライトできないことが適切に判断されます。

次の問合せでは、表timesのマテリアライズド・ビューsales_shipping_lag_mvにおいて一致する別名がありません。ただし、クエリー・リライトによって問合せとマテリアライズド・ビューの結合が比較され、timesの複数インスタンスと正しく一致します。

SELECT s.prod_id, x2.fiscal_week_number - x1.fiscal_week_number AS lag
FROM times x1, sales s, times x2
WHERE x1.time_id = s.time_id AND x2.time_id = s.time_id_ship;

デート・フォールディングを使用したクエリー・リライト

デート・フォールディングによるリライトは、式の一致によるリライトの特別な形式です。このリライトでは、問合せの日付範囲が、より大きい単位の日付で表される同等の日付範囲にフォールドされます。フォールドされた日付範囲にある大きい単位の日付で表された式は、マテリアライズド・ビューの等価の式と一致します。より大きい単位(月、四半期、年など)の日付への日付範囲のフォールドは、列の基礎となるデータ型がOracle DATEの場合に実行されます。式の一致は、式の標準形の使用をベースに実行されます。

DATEは組込みデータ型で、秒、日、月などの順序付けられた時間単位を表し、時間階層(秒->分->時間->日->月->四半期->年)が組み込まれています。DATEについてのハードコード化されたこの情報が、小さい単位の日付から大きい単位の日付に日付範囲をフォールドするときに使用されます。つまり、日付値は、月、四半期、年の初め、または月、四半期、年の終わりにフォールドできます。たとえば、日付値1-jan-1999は、年1999、四半期1999-1または月1999-01のいずれかの最初にフォールドできます。また、日付値30-sep-1999は、四半期1999-03または月1999-09のいずれかの最後にフォールドできます。

注意:
デート・フォールディングのしくみにより、BETWEENおよび日付列を使用する際には注意が必要です。BETWEENおよび日付列を使用するには、後の日付を1日増やすのが最善の方法です。つまり、date_col BETWEEN '1-jan-1999' AND '30-jun-1999'を使用するかわりに、date_col BETWEEN '1-jan-1999' AND '1-jul-1999'を使用してください。また、TRUNC(date_col) BETWEEN '1-jan-1999' AND '30-jun-1999'のように、TRUNC関数を使用しても同等の結果を取得できます。ただし、TRUNCでは、時間値が取り除かれます。

日付値は順序付けされているため、日付範囲の方がより大きい単位の整数を表している場合、日付列に対して指定されている範囲述語は小さい単位から大きい単位にフォールドできます。たとえば、範囲述語date_col >= '1-jan-1999' AND date_col < '30-jun-1999'は、日付値から特定の日付コンポーネントを抽出するTO_CHAR関数を使用して、月範囲または四半期範囲にフォールドできます。

日付値のフォールドによってデータを集計するメリットは、データを圧縮できることです。デート・フォールディングが実行されない場合、データは最小単位で集計されます。その結果、データの格納に必要なディスク領域が多くなり、マテリアライズド・ビューをスキャンするためのI/Oも増加します。

1998年の製品別の売上合計を問い合せる問合せを考えてみます。

SELECT p.prod_category, SUM(s.amount_sold)
FROM sales s, products p
WHERE s.prod_id=p.prod_id AND s.time_id >= TO_DATE('01-jan-1998', 'dd-mon-yyyy')
AND s.time_id <   TO_DATE('01-jan-1999', 'dd-mon-yyyy')
GROUP BY p.prod_category;

CREATE MATERIALIZED VIEW sum_sales_pcat_monthly_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_category, TO_CHAR(s.time_id,'YYYY-MM') AS month,
SUM(s.amount_sold) AS sum_amount
FROM sales s, products p
WHERE s.prod_id=p.prod_id
GROUP BY p.prod_category, TO_CHAR(s.time_id, 'YYYY-MM');

SELECT p.prod_category, SUM(s.amount_sold)
FROM sales s, products p
WHERE s.prod_id=p.prod_id
AND TO_CHAR(s.time_id, 'YYYY-MM') >= '01-jan-1998'
AND TO_CHAR(s.time_id, 'YYYY-MM') < '01-jan-1999'
GROUP BY p.prod_category;

SELECT mv.prod_category, mv.sum_amount
FROM sum_sales_pcat_monthly_mv mv
WHERE month >= '01-jan-1998' AND month < '01-jan-1999';

問合せに指定されている範囲は、年、四半期または月の整数を表します。prod_typeごとに事前集計された売上を含むマテリアライズド・ビューmv3が次のように定義されているとします。

CREATE MATERIALIZED VIEW mv3
ENABLE QUERY REWRITE AS
SELECT prod_name, TO_CHAR(sales.time_id,'yyyy-mm')
AS month, SUM(amount_sold) AS sum_sales
FROM sales, products WHERE sales.prod_id = products.prod_id
GROUP BY prod_name, TO_CHAR(sales_time_id, 'yyyy-mm');

問合せは、まず日付範囲を月単位にフォールドし、次に月を表す式をmv3の月式と一致させることでリライトされます。このリライトを、次の2つのステップ(日付範囲のフォールドおよび実際のリライト)で示します。

SELECT prod_name, SUM(amount_sold) AS sum_sales
FROM sales, products
WHERE sales.prod_id = products.prod_id AND TO_CHAR(sales.time_id, 'yyyy-mm') >=
TO_CHAR('01-jan-1998', 'yyyy-mm') AND TO_CHAR(sales.time_id, '01-jan-1999',
'yyyy-mm') < TO_CHAR(TO_DATE(''01-jan-1999'', ''dd-mon-yyyy''), ''yyyy-mm'')
GROUP BY prod_name;

SELECT prod_name, sum_sales
FROM mv3 WHERE month >=
TO_CHAR(TO_DATE('01-jan-1998', 'dd-mon-yyyy'), 'yyyy-mm')
AND month < TO_CHAR(TO_DATE('01-jan-1999', 'dd-mon-yyyy'), 'yyyy-mm');

mv3の事前集計済の売上がprod_nameと月ではなくprod_nameと年に基づいたものであれば、問合せは日付範囲を年単位にフォールドして年の式と一致させることでリライトされます。

ビューの制約を使用したクエリー・リライト

データ・ウェアハウス・アプリケーションでは、リレーショナル・スキーマの整合性制約を識別することにより、データベースの多次元キューブが認識されます。整合性制約は、ファクト表とディメンション表間の主キーと外部キーの関係を表します。アプリケーションは、データ・ディクショナリを問い合せれば整合性制約とデータベース内のキューブを認識できます。ただし、スキーマの複雑さとセキュリティ上の理由から、データベース管理者がファクト表とディメンション表のビューを定義する環境では、これは機能しません。このような環境では、アプリケーションはキューブを正しく識別できません。ビュー間で制約の定義を可能にすると、実表の制約をビューにも適用し、制限付きの環境でもアプリケーションにキューブを認識させることができます。

ビューの制約の定義は宣言の性質を持っていますが、ビューの操作には実表に定義された整合性制約が適用され、ビューに対する制約は実表に対する制約を通じて実施できます。データの正確性と正当性のみでなく、元のベース・オブジェクトを使用したマテリアライズド・ビューのクエリー・リライトのためにも、実表の制約を定義する必要があります。

マテリアライズド・ビューのリライトでは、クエリー・リライトのために制約が広範囲に使用されます。これは、可逆式結合の判断と、マテリアライズド・ビューの結合に問合せの結合との互換性があるかどうかの判断、リライトが可能かどうかの判断に使用されます。

ビューに対する制約の有効な状態は、DISABLE NOVALIDATEのみです。ただし、ビューの制約の状態としてRELYまたはNORELYを選択すると、より洗練されたクエリー・リライトが使用可能になります。たとえば、RELY状態のビューの制約では、問合せの整合性レベルがTRUSTEDに設定されている場合に、問合せをリライトできます。表19-3に、ビューの制約が可逆式結合の判断に使用される場合を示します。

ビューの制約は、問合せの整合性レベルがENFORCEDの場合には使用できないため注意してください。このレベルでは、最大レベルのENABLE VALIDATEが施行されます。

表19-3 ビューの制約とリライトの整合性モード

制約の状態

RELY

NORELY

ENFORCED

不可

不可

TRUSTED

可能

不可

STALE_TOLERATED

可能

不可

例19-10 ビューの制約

ビューのリライト機能を理解するには、shサンプル・スキーマを次のように拡張する必要があります。

CREATE VIEW time_view AS
SELECT time_id, TO_NUMBER(TO_CHAR(time_id, 'ddd')) AS day_in_year FROM times;

これで、ビューとファクト表間の外部キーと主キーの関係を(RELYモードで)設定できます。次の制約を追加すると、リライトは表19-3のようになります。たとえば、リライトはTRUSTEDモードで機能します。

ALTER VIEW time_view ADD (CONSTRAINT time_view_pk
PRIMARY KEY (time_id) DISABLE NOVALIDATE);
ALTER VIEW time_view MODIFY CONSTRAINT time_view_pk RELY;
ALTER TABLE sales ADD (CONSTRAINT time_view_fk FOREIGN KEY (time_id)
REFERENCES time_view(time_id) DISABLE NOVALIDATE);
ALTER TABLE sales MODIFY CONSTRAINT time_view_fk RELY;

次のマテリアライズド・ビューの定義を考えてみます。

CREATE MATERIALIZED VIEW sales_pcat_cal_day_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_category, t.day_in_year, SUM(s.amount_sold) AS sum_amount_sold
FROM time_view t, sales s, products p
WHERE t.time_id = s.time_id AND p.prod_id = s.prod_id
GROUP BY p.prod_category, t.day_in_year;

次の問合せも、ディメンション表productsが省略されていますが、主キーと外部キーの関係なしでリライトされます。これは、salesとproducts間の結合が可逆式結合であるためです。

SELECT t.day_in_year, SUM(s.amount_sold) AS sum_amount_sold
FROM time_view t, sales s WHERE t.time_id = s.time_id
GROUP BY t.day_in_year;

ただし、マテリアライズド・ビューsales_pcat_cal_day_mvがビューtime_viewでのみ定義されている場合、次の問合せはsalesとtime_viewの間の結合が明記されていないためリライトできません。これは、マテリアライズド・ビューのデルタ結合の可逆性を示せないことによります。前述のように制約が追加されていれば、この問合せもリライト可能です。

SELECT p.prod_category, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p WHERE p.prod_id = s.prod_id
GROUP BY p.prod_category;

shスキーマに加えた変更を元に戻すには、次の文を発行します。

ALTER TABLE sales DROP CONSTRAINT time_view_fk;
DROP VIEW time_view;

ビューの制約の制限

参照整合性制約定義にビューが関係する場合、つまり、ビューに外部キーまたは参照されるキーがある場合、制約に指定できるモードはDISABLE NOVALIDATEのみです。

ビューのRELY制約が許可されるのは、DISABLE NOVALIDATEモードで参照される一意または主キー制約もRELY制約である場合のみです。

参照整合性制約に対応付けるON DELETEアクションは指定できません(DELETE CASCADEなど)。ただし、ビューの制約はDISABLE NOVALIDATEモードであるため、ビューおよびその実表に対するDELETE、UPDATEおよびINSERT操作は許可されます。

集合演算子を含むマテリアライズド・ビューを使用したクエリー・リライト

集合演算子を含むマテリアライズド・ビューでクエリー・リライトを使用できます。この場合、リライトを行うために、問合せとマテリアライズド・ビューが文字どおりに一致している必要はありません。たとえば、次のマテリアライズド・ビューを考えてみます。このマテリアライズド・ビューでは、San FranciscoまたはLos Angelesの男性顧客の郵便番号を使用します。

CREATE MATERIALIZED VIEW cust_male_postal_mv
ENABLE QUERY REWRITE AS
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_gender = 'M' AND c.cust_city = 'San Francisco'
UNION ALL
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_gender = 'M' AND c.cust_city = 'Los Angeles';

次の問合せを行うと、San FranciscoまたはLos Angelesの男性顧客の郵便番号が表示されます。

SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city = 'Los Angeles' AND c.cust_gender = 'M'
UNION ALL
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city = 'San Francisco' AND c.cust_gender = 'M';

リライトされた問合せは次のようになります。

SELECT mv.cust_city, mv.cust_postal_code
FROM cust_male_postal_mv mv;

リライトされた問合せでは、UNION ALLを削除し、マテリアライズド・ビューに置き換えています。通常、クエリー・リライトでは、既存の一般的な適性ルールを使用して、UNION ALLの下位にあるSELECT副次選択が、問合せとマテリアライズド・ビューで同等かどうかを判断する必要があります。

たとえば、San Francisco、PalmdaleまたはLos Angelesの男性顧客の郵便番号を取得する問合せの場合、前述の例と同じリライトが可能ですが、クエリー・リライトでは、次のように実表でUNION ALLを保持する必要があります。

SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city= 'Palmdale' AND c.cust_gender ='M'
UNION ALL
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city = 'Los Angeles' AND c.cust_gender = 'M'
UNION ALL
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city = 'San Francisco' AND c.cust_gender = 'M';

リライトされた問合せは、次のようになります。

SELECT mv.cust_city, mv.cust_postal_code
FROM cust_male_postal_mv mv
UNION ALL
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city = 'Palmdale' AND c.cust_gender = 'M';

これにより、マテリアライズド・ビューcust_male_postal_mvを使用して、UNION ALLのサブセットをリライトできるケースがクエリー・リライトで検出されます。

UNION、UNION ALLおよびINTERSECTは可換であるため、問合せまたはマテリアライズド・ビューで副次選択が出現する順序に関係なく、クエリー・リライトでリライトできます。ただし、MINUSは可換ではありません。たとえば、A MINUS Bは、B MINUS Aと同等ではありません。したがって、問合せおよびマテリアライズド・ビューにおいてMINUS演算子の副次選択の出現順序は、リライトする順序と同じである必要があります。たとえば、customer_oldという古いバージョンの顧客表があり、London在住の男性顧客に限定した現行の顧客表との相違を探すとします。つまり、古い顧客表にはなく、現行の顧客表にあるそうした顧客を探します。MINUS演算子を使用して、これを行う方法の例を次に示します。

SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city= 'Los Angeles' AND c.cust_gender = 'M'
MINUS
SELECT c.cust_city, c.cust_postal_code
FROM customers_old c
WHERE c.cust_city = 'Los Angeles' AND c.cust_gender = 'M';

副次選択の順序を変更すると、異なる回答になります。これは、MINUSが可換でないことを示します。

UNION ALLマーカー

マテリアライズド・ビューにUNION ALL演算子が1つ以上ある場合、UNION ALLマーカーを含めることができます。UNION ALLマーカーは、マテリアライズド・ビューの各行が影響を受けているUNION ALL副次選択を識別するために使用します。クエリー・リライトでは、このマーカーを使用して、マテリアライズド・ビューのどの行が特定のUNION ALL副次選択に属しているかを識別できます。これは、問合せがマテリアライズド・ビューのデータのサブセットのみを必要とする場合、または問合せの副次選択が文字どおりマテリアライズド・ビューの副次選択と一致しない場合に有効です。例として、San Franciscoの男性顧客およびLos Angelesの女性顧客の郵便番号を取得する問合せを次に示します。

SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_gender = 'M' and c.cust_city = 'San Francisco'
UNION ALL
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_gender = 'F' and c.cust_city = 'Los Angeles';

問合せの回答は、次のマテリアライズド・ビューを使用して取得できます。

CREATE MATERIALIZED VIEW cust_postal_mv
ENABLE QUERY REWRITE AS
SELECT 1 AS marker, c.cust_gender, c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city = 'Los Angeles'
UNION ALL
SELECT 2 AS marker, c.cust_gender, c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city = 'San Francisco';

リライトされた問合せは、次のようになります。

SELECT mv.cust_city, mv.cust_postal_code
FROM cust_postal_mv mv
WHERE mv.marker = 2 AND mv.cust_gender = 'M'
UNION ALL
SELECT mv.cust_city, mv.cust_postal_code
FROM cust_postal_mv mv
WHERE mv.marker = 1 AND mv.cust_gender = 'F';

最初の副次選択のWHERE句には、mv.marker = 2およびmv.cust_gender = 'M'があります。これらは、UNION ALLの2番目の副次選択で男性顧客を表す行のみを選択します。2番目の副次選択のWHERE句には、mv.marker = 1およびmv.cust_gender = 'F'があります。これらは、UNION ALLの最初の副次選択で女性顧客を表す行のみを選択します。クエリー・リライトでは、重複行または個別行を削除する集合演算子は使用できないことに注意してください。たとえば、UNIONでは重複が削除されるため、クエリー・リライトでは削除された行を識別できません。次に例を示します。

SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city= 'Palmdale' AND c.cust_gender ='M'
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_gender = 'M' and c.cust_city = 'San Francisco'
UNION ALL
SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_gender = 'F' and c.cust_city = 'Los Angeles';

UNION ALLマーカーを使用してリライトされた問合せは、次のようになります。

SELECT c.cust_city, c.cust_postal_code
FROM customers c
WHERE c.cust_city= 'Palmdale' AND c.cust_gender ='M'
UNION ALL
SELECT mv.cust_city, mv.cust_postal_code

FROM cust_postal_mv mv
WHERE mv.marker = 2 AND mv.cust_gender = 'M'
UNION ALL
SELECT mv.cust_city, mv.cust_postal_code
FROM cust_postal_mv mv
WHERE mv.marker = 1 AND mv.cust_gender = 'F';

マーカーを使用する場合に準拠する必要があるルールを次に示します。

- 定数または文字列を指定し、すべてのUNION ALL副次選択と同じデータ型にする必要があります。

- 定数を指定し、UNION ALL副次選択ごとに別個の値にします。複数の副次選択で同じ値は再使用できません。

- すべての副次選択に対して同じ順序の場所に指定する必要があります。

グルーピング・セットがある場合のクエリー・リライト

この項では、グルーピング・セットがある場合のクエリー・リライトの使用について、考慮すべき事柄をいくつか説明します。

GROUP BY拡張機能を使用したクエリー・リライト

GROUP BY句の拡張機能としてGROUPING SETS、CUBE、ROLLUPおよびこれらの連結が使用できます。これらの拡張機能により、必要なグルーピングを問合せのGROUP BY句で選択的に指定できます。たとえば、次の例は、グルーピング・セットを使用した一般的な問合せです。

SELECT p.prod_subcategory, t.calendar_month_desc, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, customers c, products p, times t
WHERE s.time_id=t.time_id AND s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY GROUPING SETS ((p.prod_subcategory, t.calendar_month_desc),
(c.cust_city, p.prod_subcategory));

GROUP BY拡張機能を持つ問合せに対するベース・グルーピングという語は、GROUP BY句に存在するすべての一意式を意味します。前述の問合せでは、グルーピング(p.prod_subcategory, t.calendar_month_desc, c.cust_city)がベース・グルーピングです。

拡張機能は、ユーザーの問合せやマテリアライズド・ビューを定義する問合せに使用できます。いずれの場合も、マテリアライズド・ビューのリライトが適用され、リライトの機能を次の使用例に分けて識別できます。

- マテリアライズド・ビューが単純GROUP BYを持ち、問合せが拡張GROUP BYを持つ場合

- マテリアライズド・ビューが拡張GROUP BYを持ち、問合せが単純GROUP BYを持つ場合

- マテリアライズド・ビューと問合せの両方が拡張GROUP BYを持つ場合

マテリアライズド・ビューが単純GROUP BYを持ち、問合せが拡張GROUP BYを持つ場合

問合せに拡張GROUP BY句が含まれている場合、ベース・グルーピングが「Oracleによるクエリー・リライト条件」で説明したリライト・ルールにリストされているマテリアライズド・ビューを使用してリライトできる場合は、マテリアライズド・ビューでリライトできます。たとえば、次の問合せを考えてみます。

SELECT p.prod_subcategory, t.calendar_month_desc, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, customers c, products p, times t
WHERE s.time_id=t.time_id AND s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY GROUPING SETS
((p.prod_subcategory, t.calendar_month_desc),
(c.cust_city, p.prod_subcategory));

ベース・グルーピングは(p.prod_subcategory, t.calendar_month_desc, c.cust_city, p.prod_subcategory))であり、これによってOracleでは、次のようにsum_sales_pscat_month_city_mvを使用して問合せをリライトできます。

SELECT mv.prod_subcategory, mv.calendar_month_desc, mv.cust_city,
SUM(mv.sum_amount_sold) AS sum_amount_sold
FROM sum_sales_pscat_month_city_mv mv
GROUP BY GROUPING SETS
((mv.prod_subcategory, mv.calendar_month_desc),
(mv.cust_city, mv.prod_subcategory));

問合せでEXPAND_GSET_TO_UNIONヒントを使用している場合は、特殊な状況が発生します。EXPAND_GSET_TO_UNIONの使用例は、「拡張GROUP BYを持つ問合せのヒント」を参照してください。

マテリアライズド・ビューが拡張GROUP BYを持ち、問合せが単純GROUP BYを持つ場合

拡張GROUP BYを持つマテリアライズド・ビューをリライトに使用するためには、さらに2つの条件を満たしている必要があります。

- グルーピング識別名が含まれていること。これは、すべてのGROUP BY式のGROUPING_ID関数です。たとえば、マテリアライズド・ビューのGROUP BY句がGROUP BY CUBE(a, b)の場合は、SELECT構文のリストにGROUPING_ID(a, b)を含める必要があります。

- マテリアライズド・ビューのGROUP BYの結果、重複するグルーピングが発生しないこと。たとえば、GROUP BY GROUPING SETS ((a, b), (a, b))があると、マテリアライズド・ビューは一般的なリライトの対象外になります。

拡張GROUP BYを持つマテリアライズド・ビューには、複数のグルーピングが含まれています。Oracleは、問合せを計算できる最低コストのグルーピングを検索し、それをリライトに使用します。たとえば、次のマテリアライズド・ビューを考えてみます。

CREATE MATERIALIZED VIEW sum_grouping_set_mv
ENABLE QUERY REWRITE AS
SELECT p.prod_category, p.prod_subcategory, c.cust_state_province, c.cust_city,
GROUPING_ID(p.prod_category,p.prod_subcategory,
c.cust_state_province,c.cust_city) AS gid,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY GROUPING SETS
((p.prod_category, p.prod_subcategory, c.cust_city),
(p.prod_category, p.prod_subcategory, c.cust_state_province, c.cust_city),
(p.prod_category, p.prod_subcategory));

この場合、次の問合せはリライトされます。

SELECT p.prod_subcategory, c.cust_city, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY p.prod_subcategory, c.cust_city;

この問合せは、マテリアライズド・ビューに最も近似したグルーピングでリライトされます。つまり、次のように、(prod_category, prod_subcategory, cust_city)グルーピングが使用されます。

SELECT prod_subcategory, cust_city, SUM(sum_amount_sold) AS sum_amount_sold
FROM sum_grouping_set_mv
WHERE gid = grouping identifier of (prod_category,prod_subcategory, cust_city)
GROUP BY prod_subcategory, cust_city;

マテリアライズド・ビューと問合せの両方が拡張GROUP BYを持つ場合

マテリアライズド・ビューと問合せの両方にGROUP BY拡張機能が含まれている場合、Oracleではリライトに対してグルーピングの一致とUNION ALLリライトという2つの方法が使用されます。最初にグルーピングの一致が試されます。問合せのグルーピングがマテリアライズド・ビューのグルーピングと一致し、しかもロールアップなしで一致する場合、マテリアライズド・ビューからグルーピングが選択されます。たとえば、次の問合せを考えてみます。

SELECT p.prod_category, p.prod_subcategory, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY GROUPING SETS
((p.prod_category, p.prod_subcategory, c.cust_city),
(p.prod_category, p.prod_subcategory));

この問合せは、sum_grouping_set_mvの2つのグルーピングと一致するため、Oracleは問合せを次のようにリライトします。

SELECT prod_subcategory, cust_city, sum_amount_sold
FROM sum_grouping_set_mv
WHERE gid = grouping identifier of (prod_category,prod_subcategory, cust_city)
OR gid = grouping identifier of (prod_category,prod_subcategory)

グルーピングの一致が失敗した場合、UNION ALLリライトと呼ばれる一般的なリライト方法が試みられます。Oracleは、最初に、拡張GROUP BY句を持つ問合せを、同等なUNION ALL問合せで表します。元の問合せのグルーピングはすべて別個のUNION ALLブランチで置き換えられます。ブランチは、単純GROUP BY句を持ちます。たとえば、次の問合せを考えてみます。

SELECT p.prod_category, p.prod_subcategory, c.cust_state_province,
t.calendar_month_desc, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c, times t
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY GROUPING SETS
((p.prod_subcategory, t.calendar_month_desc),
(t.calendar_month_desc),
(p.prod_category, p.prod_subcategory, c.cust_state_province),
(p.prod_category, p.prod_subcategory));

これは、最初に、次の4つのブランチを持つUNION ALLで表されます。

SELECT null, p.prod_subcategory, null,
t.calendar_month_desc, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c, times t
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY p.prod_subcategory, t.calendar_month_desc
UNION ALL
SELECT null, null, null,
t.calendar_month_desc, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c, times t
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY t.calendar_month_desc
UNION ALL
SELECT p.prod_category, p.prod_subcategory, c.cust_state_province,
null, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c, times t
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY p.prod_category, p.prod_subcategory, c.cust_state_province
UNION ALL
SELECT p.prod_category, p.prod_subcategory, null,
null, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c, times t
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY p.prod_category, p.prod_subcategory;

次に、個々のブランチは、「Oracleによるクエリー・リライト条件」のルールを使用して個別にリライトされます。マテリアライズド・ビューsum_grouping_set_mvを使用して、ブランチ3(マテリアライズド・ビューのロールアップが必要)とブランチ4(マテリアライズド・ビューと正確に一致)のみがリライトできます。リライトされないブランチは、元の拡張GROUP BY形式に変換されます。この結果、問合せは次のようにリライトされます。

SELECT null, p.prod_subcategory, null,
t.calendar_month_desc, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c, times t
WHERE s.prod_id = p.prod_id AND s.cust_id = c.cust_id
GROUP BY GROUPING SETS
((p.prod_subcategory, t.calendar_month_desc),
(t.calendar_month_desc),)
UNION ALL
SELECT prod_category, prod_subcategory, cust_state_province,
null, SUM(sum_amount_sold) AS sum_amount_sold
FROM sum_grouping_set_mv
WHERE gid = <grouping id of (prod_category,prod_subcategory, cust_city)>
GROUP BY p.prod_category, p.prod_subcategory, c.cust_state_province
UNION ALL
SELECT prod_category, prod_subcategory, null,
null, sum_amount_sold
FROM sum_grouping_set_mv
WHERE gid = <grouping id of (prod_category,prod_subcategory)>

拡張GROUP BYを持つ問合せは等価なUNION ALLで表され、リライトの最適化のために再帰的に実行されることに注意してください。リライトできないグルーピングはUNION ALLの最後のブランチに残り、かわりに実データがアクセスされます。

拡張GROUP BYを持つ問合せのヒント

EXPAND_GSET_TO_UNIONヒントを使用すると、GROUP BY拡張機能を持つ問合せを、等価なUNION ALL問合せへ強制的に拡張できます。このヒントは、マテリアライズド・ビューが単純GROUP BY句のみを持つ環境で使用できます。この場合、各ブランチが別個のマテリアライズド・ビューで独立してリライトできるため、リライトの柔軟性が増します。EXPAND_GSET_TO_UNIONの詳細は、『Oracle Databaseパフォーマンス・チューニング・ガイド』を参照してください。

ウィンドウ関数がある場合のクエリー・リライト

ウィンドウ関数は、累積集計、移動集計および集中集計の計算に使用されます。これらの関数は、SUM、AVG、MIN、MAX、COUNT、VARIANCE、STDDEV、FIRST_VALUEおよびLAST_VALUEの各集計とともに使用されます。ウィンドウ関数を持つ問合せは、テキストの完全一致のリライトを使用してリライトできます。これには、マテリアライズド・ビューの定義も問合せに完全に一致する必要があります。マテリアライズド・ビューにウィンドウ関数がなく、問合せ内の集計がマテリアライズド・ビューにあり、結合互換性チェックなどの他のすべての適格性チェックにパスした場合に、ウィンドウ関数を持つ問合せはリライトできます。問合せのウィンドウ関数は、マテリアライズド・ビューのウィンドウ関数と比較されます。比較には標準的な形式のフォーマットが使用されます。これによって、複雑なウィンドウ関数でもリライトを実行できます。

クエリー・リライトの際に、ウィンドウ関数を持つ問合せでロールアップが必要となる場合、その問合せは、可能な場合、集計を持つ内部問合せとウィンドウ関数を持つ外部問合せに分割されます。これにより、クエリー・リライトでは、ウィンドウ関数が適用される前に内部問合せの集計をリライトできます。唯一の例外は、問合せにウィンドウ関数とグルーピング・セットの両方がある場合です。この場合、グルーピング・セットがあるため、クエリー・リライトでは問合せを分割できず、問合せはリライトされません。

クエリー・リライトおよび式の一致

マテリアライズド・ビューの列が、問合せの式と一致する事前に計算された式を表している場合、問合せに使用される式は、マテリアライズド・ビューの単純列に置き換えることができます。マテリアライズド・ビューを使用するように問合せをリライトできれば、その方が高速になります。これは、マテリアライズド・ビューには事前計算済の計算が含まれており、式の計算を実行する必要がないためです。

式の一致は、まず式を標準的な形式に変換し、次にそれらが同等かどうかを比較することによって行われます。したがって、2つの異なる式は、互いに同等であれば通常は一致します。さらに、問合せの式全体がマテリアライズド・ビュー内の式と一致しなかった場合は、副式内の一致が検索されます。式を最大限に一致させるために、副式はトップダウンで検索されます。

年齢の範囲(1-10、11-20、21-30など)ごとの売上の合計を問い合せる問合せについて考えてみます。

CREATE MATERIALIZED VIEW sales_by_age_bracket_mv
ENABLE QUERY REWRITE AS
SELECT TO_CHAR((2000-c.cust_year_of_birth)/10-0.5,999) AS age_bracket,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, customers c WHERE s.cust_id=c.cust_id
GROUP BY TO_CHAR((2000-c.cust_year_of_birth)/10-0.5,999);

次の問合せは式の一致を使用してリライトされます。

SELECT TO_CHAR(((2000-c.cust_year_of_birth)/10)-0.5,999), SUM(s.amount_sold)
FROM sales s, customers c WHERE s.cust_id=c.cust_id
GROUP BY TO_CHAR((2000-c.cust_year_of_birth)/10-0.5,999);

この問合せは、次のような年齢の範囲の式(つまり、2000 - c.cust_year_of_birth)/10-0.5)の標準的な形式の一致に基づいてsales_by_age_bracket_mvでリライトされます。

SELECT age_bracket, sum_amount_sold FROM sales_by_age_bracket_mv;

部分的に失効したマテリアライズド・ビューを使用したクエリー・リライト

ディテール表のあるパーティションが更新されると、マテリアライズド・ビューの特定セクションのみが失効としてマークされます。マテリアライズド・ビューには、その特定の行またはグループに対応する表のパーティションを識別できる情報が必要です。最も単純な使用例は、マテリアライズド・ビューのSELECT構文のリスト内で表のパーティション化キーを使用する場合です。これが、行を失効パーティションにマップするには最も簡単な方法であるためです。部分的に失効したマテリアライズド・ビューを使用する場合のポイントは、次のとおりです。

- クエリー・リライトでENFORCEDまたはTRUSTEDモードのマテリアライズド・ビューを使用できるのは、そのマテリアライズド・ビューのうち問合せに対する回答に使用される行がFRESHであると認識される場合です。

- マテリアライズド・ビュー内の最新行は、そのビューのWHERE句に選択述語を追加することで識別されます。回答がこの(制限付き)マテリアライズド・ビュー内に含まれる場合は、このマテリアライズド・ビューで問合せをリライトできます。

ファクト表salesは、次のようにtime_idの範囲に基づいてパーティション化されます。

PARTITION BY RANGE (time_id)
(PARTITION SALES_Q1_1998
VALUES LESS THAN (TO_DATE('01-APR-1998', 'DD-MON-YYYY')),
PARTITION SALES_Q2_1998
VALUES LESS THAN (TO_DATE('01-JUL-1998', 'DD-MON-YYYY')),
PARTITION SALES_Q3_1998
VALUES LESS THAN (TO_DATE('01-OCT-1998', 'DD-MON-YYYY')),
...

次のように、time_idでグルーピングされているマテリアライズド・ビューがあるとします。

CREATE MATERIALIZED VIEW sum_sales_per_city_mv
ENABLE QUERY REWRITE AS
SELECT s.time_id, p.prod_subcategory, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id
GROUP BY time_id, prod_subcategory, cust_city;

また、パーティションsales_q4_2000に割り当てられる2000年12月に関する新規データが挿入されるとします。テストの目的で、salesに対して任意のDML操作を適用し、このマテリアライズド・ビューが最新の場合に次の問合せがsales_q1_2000のデータを要求すると、これ以外のパーティションを変更できます。たとえば、次のようにします。

INSERT INTO SALES VALUES(17, 10, '01-DEC-2000', 4, 380, 123.45, 54321);

リフレッシュが完了するまで、マテリアライズド・ビューは一般に失効状態で、enforcedモードでの無制限のリライトには使用できません。ただし、表salesはパーティション化されており、すべてのパーティションが変更されたわけではないため、変更されていないパーティションはすべてOracleによって識別できます。オプティマイザは、次のように問合せを定義するマテリアライズド・ビューに選択述語を暗黙的に追加することにより、マテリアライズド・ビューの最新行(最後のリフレッシュ操作以降に行われた更新の影響を受けないデータ)を識別できます。

SELECT s.time_id, p.prod_subcategory, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id
AND   s.time_id < TO_DATE('01-OCT-2000','DD-MON-YYYY')
OR s.time_id >= TO_DATE('01-OCT-2001','DD-MON-YYYY'))
GROUP BY time_id, prod_subcategory, cust_city;

部分的に失効したマテリアライズド・ビューが最新であるかどうかは、論理ベースではなくパーティションごとに追跡されることに注意してください。salesファクト表のパーティション化方法は四半期ベースであるため、2000年12月に変更があると、パーティションsales_q4_2000全体が失効します。

2000年の第1四半期と第2四半期の売上を要求する次の問合せがあるとします。

SELECT s.time_id, p.prod_subcategory, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id
AND   s.time_id BETWEEN TO_DATE('01-JAN-2000', 'DD-MON-YYYY')
AND TO_DATE('01-JUL-2000', 'DD-MON-YYYY')
GROUP BY time_id, prod_subcategory, cust_city;

Oracle Databaseでは、マテリアライズド・ビュー内の該当する行範囲が最新であるとわかっているため、前述の問合せをマテリアライズド・ビューでリライトできます。リライトされた問合せは、次のようになります。

SELECT time_id, prod_subcategory, cust_city, sum_amount_sold
FROM sum_sales_per_city_mv
WHERE time_id BETWEEN TO_DATE('01-JAN-2000', 'DD-MON-YYYY')
AND TO_DATE('01-JUL-2000', 'DD-MON-YYYY');

マテリアライズド・ビューのSELECT(およびGROUP BYリスト)には、パーティン化キーのかわりにパーティション・マーカー(ROWIDを持つパーティションを識別するファンクション)を使用できます。マテリアライズド・ビューを使用すると、パーティション全体を含むパーティション化キーの範囲を指定する選択述語がある問合せなど、特定のパーティション(パーティション・マーカーで識別可能)のデータのみを必要とする問合せをリライトできます。パーティション・マーカー・ファンクションDBMS_MVIEW.PMARKERの詳細は、第10章「高度なマテリアライズド・ビュー」を参照してください。

次の例は、パーティション・キー列を直接使用するのではなく、マテリアライズド・ビューでパーティション・マーカーを使用する方法を示しています。

CREATE MATERIALIZED VIEW sum_sales_per_city_2_mv
ENABLE QUERY REWRITE AS
SELECT DBMS_MVIEW.PMARKER(s.rowid) AS pmarker,
t.fiscal_quarter_desc, p.prod_subcategory, c.cust_city,
SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id
AND   s.time_id = t.time_id
GROUP BY DBMS_MVIEW.PMARKER(s.rowid),
p.prod_subcategory, c.cust_city, t.fiscal_quarter_desc;

パーティションsales_q1_2000が最新であり、sales表の他のパーティションに対してDML変更が行われたことがわかっているとします。テストの目的で、salesに対して任意のDML操作を適用し、マテリアライズド・ビューが最新のときにsales_q1_2000以外のパーティションを変更できます。次に例を示します。

INSERT INTO SALES VALUES(17, 10, '01-DEC-2000', 4, 380, 123.45, 54321);

マテリアライズド・ビューsum_sales_per_city_2_mvは、これで一般に失効しているとみなされますが、Oracle Databaseでは、このマテリアライズド・ビューを使用して次の問合せをリライトできます。この問合せは、次に示すように、データをパーティションsales_q1_2000に制限し、cust_cityの特定値のみを選択します。

SELECT p.prod_subcategory, c.cust_city, SUM(s.amount_sold) AS sum_amount_sold
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id AND s.time_id = t.time_id
AND c.cust_city= 'Nuernberg'
AND s.time_id >=TO_DATE('01-JAN-2000','dd-mon-yyyy')
AND s.time_id <  TO_DATE('01-APR-2000','dd-mon-yyyy')
GROUP BY prod_subcategory, cust_city;

PMARKERファンクションを含む部分的に失効したマテリアライズド・ビューでのリライトは、前述の例で示すように、1つ以上のパーティションの完全なデータ内容がアクセスされ、述語条件がパーティション化されたファクト表自体にある場合にのみ実行できることに注意してください。

DBMS_MVIEW.PMARKERファンクションでは、各パーティションごとにまったく別の値が与えられます。これにより、パーティション化キー自体と比べて、潜在的なマテリアライズド・ビュー内の行数は大幅に減りますが、このキーに関する詳細な情報もすべて放棄することになります。わかっている情報は、パーティション番号、したがって、境界値の上限と下限のみです。これは、レンジ・パーティション列のカーディナリティ、つまり行数を減らすこととのトレードオフとなります。

パーティションsales_q1_2000のp_marker値を31070とすると、前述の問合せはマテリアライズド・ビューに対して次のようにリライトできます。

SELECT mv.prod_subcategory, mv.cust_city, SUM(mv.sum_amount_sold)
FROM sum_sales_per_city_2_mv mv
WHERE mv.pmarker = 31070 AND mv.cust_city= 'Nuernberg'
GROUP BY prod_subcategory, cust_city;

したがって、問合せは失効データにアクセスせずにマテリアライズド・ビューに対してリライトできます。

カーソルの共有とバインド変数

クエリー・リライトがサポートされるのは、問合せにユーザー・バインド変数が含まれ、実際のバインド値がクエリー・リライトで不要な場合です。バインド変数の実際の値がクエリー・リライトで必要な場合、このクエリー・リライトはバインド値に依存しています。クエリー・リライト時にユーザー・バインド変数は使用できないため、クエリー・リライトがバインド値に依存している場合、問合せのリライトはできません。例として、次のマテリアライズド・ビューcustomer_mvを考えてみます。このマテリアライズド・ビューには、WHERE句に述語(customer_id >= 1000)があります。

CREATE MATERIALIZED VIEW customer_mv
ENABLE QUERY REWRITE AS
SELECT cust_id, prod_id,  SUM(amount_sold) AS total_amount
FROM sales WHERE cust_id >= 1000
GROUP BY cust_id, prod_id;

次の問合せを考えてみます。この問合せには、WHERE句にユーザー・バインド変数(:user_id)があります。

SELECT cust_id, prod_id, SUM(amount_sold) AS sum_amount
FROM sales WHERE cust_id > :user_id
GROUP BY cust_id, prod_id;

マテリアライズド・ビューcustomer_mvには、WHERE句に選択述語があるため、クエリー・リライトは、ユーザー・バインド変数user_idの実際の値に依存して包含を計算します。user_idはクエリー・リライト時に使用できず、クエリー・リライトはuser_idのバインド値に依存しているため、この問合せはリライトできません。

前述の例ではWHERE句にユーザー・バインド変数がありますが、ユーザー・バインド変数が問合せのどの部分にあっても、結果は同じです。つまり、ユーザー・バインド変数が問合せのどの箇所にあるかに関係なく、クエリー・リライトがその値に依存している場合、その問合せはリライトできません。

次の問合せを考えてみます。この問合せでは、SELECT構文のリストにユーザー・バインド変数(:user_id)があります。

SELECT cust_id + :user_id, prod_id, SUM(amount_sold) AS total_amount
FROM sales WHERE cust_id >= 2000
GROUP BY cust_id, prod_id;

この場合、ユーザー・バインド変数user_idの値はクエリー・リライト時に必要ないため、前述の問合せはリライトされます。

SELECT cust_id + :user_id, prod_id, total_amount
FROM customer_mv;

クエリー・リライトでの式の処理

式がTO_DATE('12-SEP-1999','DD-Mon-YYYY')のように定数に評価される場合は、一部の式でのリライトもサポートされます。たとえば、既存のマテリアライズド・ビューが次のように定義されているとします。

CREATE MATERIALIZED VIEW sales_on_valentines_day_99_mv
BUILD IMMEDIATE
REFRESH FORCE
ENABLE QUERY REWRITE AS
SELECT s.prod_id, s.cust_id, s.amount_sold
FROM times t, sales s WHERE s.time_id = t.time_id
AND t.time_id = TO_DATE('14-FEB-1999', 'DD-MON-YYYY');

したがって、次の問合せはリライトできます。

SELECT s.prod_id, s.cust_id, s.amount_sold
FROM sales s, times t WHERE s.time_id = t.time_id
AND t.time_id = TO_DATE('14-FEB-1999', 'DD-MON-YYYY');

この問合せは、次のようにリライトされます。

SELECT * FROM sales_on_valentines_day_99_mv;

TO_DATEが使用される際は常に、与えられた日付マスクがNLS_DATE_FORMATで指定された日付マスクと同じである場合にのみクエリー・リライトが発生します。

同等化を使用した高度なクエリー・リライト

2つのSQL文が機能的に同等であることを宣言できる場合に使用可能な特殊なタイプのクエリー・リライトがあります。この機能を使用すると、内部アプリケーション情報をデータベースに配置でき、データベースがこの情報を活用することにより、問合せのパフォーマンスが向上します。これを実行するには、機能的に同等な(同じ行と列を戻す)SELECT文を2つ宣言して、一方のSELECT文がパフォーマンスに関して有利なように指定します。

この高度なリライトの機能は通常、様々な問合せパフォーマンスの問題や事項に適用できます。この機能はどのようなアプリケーションでも使用でき、複雑なユーザー問合せに対するリライトに作用して、一般的に内部アプリケーション情報を持つユーザーが特別に作成した、より単純でパフォーマンスの高い問合せで回答を得ることができます。

SQL文の変換および飛躍的なパフォーマンス向上のためのチューニングを実現する、内部アプリケーション情報を持つことができるシナリオには様々なものがあります。実行する最適化のタイプには、非常に単純なものや、問合せを大幅に再構成するような高度なものもあります。ただし、入力SQL問合せはアプリケーションで生成される場合が多く、このような問合せの形式および構造は制御できません。

この機能へのアクセス権を取得するには、SYSDBAとして接続し、リライトの同等化を宣言するデータベース管理者に実行アクセス権を明示的に付与する必要があります。詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

この項では、このようなタイプの高度なリライト機能を理解するために、一部の例で多次元データを使用しています。リソース使用率を最適化するため、アプリケーションでは、複雑なSQL、カスタムCコードまたはテーブル・ファンクションを使用して、データベースからデータを取得する場合があります。ただし、エンド・ユーザーに関するかぎり、こうした複雑な操作を行うことはほとんどありません。ユーザーにとっては、SELECT ... GROUP BYなどの一般的な問合せで回答を取得するのが理想的です。

次の例では、指定の代替問合せを使用して特定のユーザー問合せを実行する必要があることを、Oracleに対して宣言します。Oracleはこの関係を認識し、ユーザーから問合せが要求されるたびに、代替問合せを使用して透過的にリライトします。したがって、ユーザーは複雑な集計計算のSQLを学習したり記述したりする必要はありません。

例19-11 同等化を使用したリライト

ここにsales_factおよびgeog_dimという2つの実表があります。次の文を発行すると、ロールアップを使用して市、州および地域ごとの合計売上を計算できます。

SELECT g.region, g.state, g.city,
GROUPING_ID(g.city, g.state, g.region), SUM(sales)
FROM sales_fact f, geog_dim g WHERE f.geog_key = g.geog_key
GROUP BY ROLLUP(g.region, g.state, g.city);

アプリケーションでは、迅速に結果を戻せるように、この問合せがマテリアライズされることもあります。ただし、生成されるマテリアライズド・ビューによって、非常に多くのディスク領域が占有されてしまいます。ただし、市から州、州から地域にロールアップするディメンションがある場合、次のようにDECODE文を使用して3つのグルーピング列を1つの列に簡単に圧縮できます。これは、埋込み合計と呼ばれます。

DECODE (gid, 0, city, 1, state, 3, region, 7, "grand_total")

この場合、最低レベルの階層を使用して、情報全体を表します。たとえば、BostonがBoston, MA, New England Regionを表し、CAがCA, Western Regionを表す場合などです。アプリケーションでは、この埋込み合計の結果をembedded_total_salesなどの表に格納します。

ただし、結果を取得する際に、すべてのデータ列(city、state、region)が必要な場合もあります。アプリケーションでは、結果を効率的かつ迅速に戻すため、次のようにカスタム・テーブル・ファンクション(et_function)を使用して、表embedded_total_salesから拡張形式でデータを取り出す場合があります。

SELECT * FROM TABLE (et_function);

つまりこの機能によって、アプリケーションでは、前述のユーザー問合せと代替問合せの同等化を次のように宣言できるようになります。

DBMS_ADVANCED_REWRITE.DECLARE_REWRITE_EQUIVALENCE (
'EMBEDDED_TOTAL',
'SELECT g.region, g.state, g.city,
GROUPING_ID(g.city, g.state, g.region), SUM(sales)
FROM sales_fact f, geog_dim g
WHERE f.geog_key = g.geog_key
GROUP BY ROLLUP(g.region, g.state, g.city)',
'SELECT * FROM TABLE(et_function)');

このDECLARE_REWRITE_EQUIVALENCEの呼出しにより、EMBEDDED_TOTALという同等化の宣言が作成されます。この宣言では、指定されたSOURCE_STMTおよびDESTINATION_STMTが機能的に同等であり、パフォーマンスに関してはDESTINATION_STMTが優先されることを示しています。いったん、このような宣言をDBAが作成すれば、ユーザーは内部で実行されている領域の最適化に関する知識を持つ必要がありません。

またこの機能により、アプリケーションでは、SQL問合せの特殊な部分的なマテリアライズを実行できます。たとえば、例19-12で示す3つのリレーションのUNION ALLを使用して、ロールアップを実行することもできます。

例19-12 同等化を使用したリライト(UNION ALL)

CREATE MATERIALIZED VIEW T1
AS SELECT g.region, g.state, g.city, 0 AS gid, SUM(sales) AS sales
FROM sales_fact f, geog_dim g WHERE f.geog_key = g.geog_key
GROUP BY g.region, g.state, g.city;

CREATE MATERIALIZED VIEW T2 AS
SELECT t.region, t.state, SUM(t.sales) AS sales
FROM T1 GROUP BY t.region, t.state;

CREATE VIEW T3 AS
SELECT t.region, SUM(t.sales) AS sales
FROM T2 GROUP BY t.region;

ROLLUP(region, state, city)問合せは、次のものと同等になります。

SELECT * FROM T1 UNION ALL
SELECT region, state, NULL, 1 AS gid, sales FROM T2 UNION ALL
SELECT region, NULL, NULL, 3 AS gid, sales FROM T3 UNION ALL
SELECT NULL, NULL, NULL, 7 AS gid, SUM(sales) FROM T3;

この同等化を指定することにより、Oracle Databaseは問合せの効率的な2番目の形式を使用して、ユーザーが問い合せたROLLUP問合せを計算できます。

DBMS_ADVANCED_REWRITE.DECLARE_REWRITE_EQUIVALENCE (
'CUSTOM_ROLLUP',
'SELECT g.region, g.state, g.city,
GROUPING_ID(g.city, g.state, g.region), SUM(sales)
FROM sales_fact f, geog_dim g
WHERE f.geog_key = g.geog_key
GROUP BY ROLLUP(g.region, g.state, g.city ',
' SELECT * FROM T1
UNION ALL
SELECT region, state, NULL, 1 as gid, sales FROM T2
UNION ALL
SELECT region, NULL, NULL, 3 as gid, sales FROM T3
UNION ALL
SELECT NULL, NULL, NULL, 7 as gid, SUM(sales) FROM T3');

また、この機能を使用すると、概念的には単純でもSQLで表現すると非常に複雑になる特殊な集計計算が可能になります。この場合アプリケーションは、指定のカスタム集計関数を使用し、内部的に複雑なSQLで計算を行うよう、ユーザーへ通知します。

例19-13 同等化を使用したリライト(カスタム集計を使用)

アプリケーション・ユーザーが市、州、地域ごとの売上および特定の季節の売上情報を調べるとします。たとえば、New Englandのユーザーが、冬季の各月におけるNew Englandの各市の売上情報を必要としているとします。アプリケーションでは、前述の集計を計算する特殊な集計Seasonal_Aggが提供されます。この場合、従来のサマリー問合せを行いますが、SUM(sales)ではなくSeasonal_Agg(sales, region)を使用します。

SELECT g.region, t.calendar_month_name, Seasonal_Agg(f.sales, g.region) AS sales
FROM sales_fact f, geog_dim g, times t
WHERE f.geog_key = g.geog_key AND f.time_id = t.time_id
GROUP BY g.region, t.calendar_month_name;

アプリケーションでは、ユーザーが追加の計算を行うSQLを記述するよう指示されることはなく、この機能により自動的に計算が行われます。この例の場合、Seasonal_Aggはスプレッドシート機能を使用して計算されます(第23章「モデリングのSQL」を参照)。Seasonal_Aggはユーザー定義の集計ですが、問合せの回答に行を追加することが必要となります。このような処理は、単純なPL/SQLファンクションでは容易には行えません。

DBMS_ADVANCED_REWRITE.DECLARE_REWRITE_EQUIVALENCE (
'CUSTOM_SEASONAL_AGG',
SELECT g.region, t.calendar_month_name, Seasonal_Agg(sales, region) AS sales
FROM sales_fact f, geog_dim g, times t
WHERE f.geog_key = g.geog_key AND f.time_id = t.time_id
GROUP BY g.region, t.calendar_month_name',
'SELECT g,region, t.calendar_month_name, SUM(sales) AS sales
FROM sales_fact f, geog_dim g
WHERE f.geog_key = g.geog_key AND t.time_id = f.time_id
GROUP BY g.region, g.state, g.city, t.calendar_month_name
DIMENSION BY g.region, t.calendar_month_name
(sales ['New England', 'Winter'] = AVG(sales) OVER calendar_month_name IN
('Dec', 'Jan', 'Feb', 'Mar'),
sales ['Western', 'Summer' ] = AVG(sales) OVER calendar_month_name IN
('May', 'Jun', 'July', 'Aug'), .);

同等化を使用した結果キャッシュ・マテリアライズド・ビューの作成

結果キャッシュ・マテリアライズド・ビュー(RCMV)という特殊なマテリアライズド・ビューを使用すると、クエリー・リライトの実行時に結果キャッシュを使用できます。結果キャッシュ・マテリアライズド・ビューでは、より少ない領域ですばやくアクセスできるという結果キャッシュの主なメリットを得られる上に、クエリー・リライトを実行できないという通常のデメリットから解放されます。

このタイプのマテリアライズド・ビューの使用例を次に示します。

例19-14 結果キャッシュ・マテリアライズド・ビュー

まず、必要な権限を付与します。

CONNECT / AS SYSDBA
GRANT CREATE MATERIALIZED VIEW TO sh;
GRANT EXECUTE ON DBMS_ADVANCED_REWRITE TO sh;

次に、結果キャッシュ・マテリアライズド・ビューを作成します。

CONNECT sh/sh
begin
sys.DBMS_ADVANCED_REWRITE.Declare_Rewrite_Equivalence
(
Name               => 'RCMV_SALES',
Source_Stmt        =>
'select channel_id, prod_id, sum(amount_sold), count(amount_sold)
from sales
group by prod_id, channel_id',
Destination_Stmt   =>
'select * from
(select /*+ RESULT_CACHE(name=RCMV_SALES) */
channel_id, prod_id, sum(amount_sold), count(amount_sold)
from sales
group by prod_id, channel_id)',
Validate         => FALSE,
Rewrite_Mode     => 'GENERAL'
);
end;
/

ALTER SESSION SET query_rewrite_integrity = stale_tolerated;

EXPLAIN PLANを見ながら、異なるすべての問合せがRCMV_SALESにリライトされることを確認します。

EXPLAIN PLAN FOR
SELECT channel_id, SUM(amount_sold) FROM sales GROUP BY channel_id;
@?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3903632134
--------------------------------------------------------------------------------
|Id |         Operation       | Name  |Rows|Bytes|Cost(%CPU)| Time |Pstart|Pstop|
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT        |       |  4 |   64| 1340 (68)|00:00:17|    |    |
| 1 |  HASH GROUP BY          |       |  4 |   64| 1340 (68)|00:00:17|    |    |
| 2 |   VIEW                  |       | 204| 3264| 1340 (68)|00:00:17|    |    |
| 3 |    RESULT CACHE         |3gps5zr86gyb53y36js9zuay2s| | | |     |    |    |
| 4 |     HASH GROUP BY       |       | 204| 2448| 1340 (68)|00:00:17|    |    |
| 5 |      PARTITION RANGE ALL|       |918K|  10M|  655 (33)|00:00:08|  1 | 28 |
| 6 |       TABLE ACCESS FULL | SALES |918K|  10M|  655 (33)|00:00:08|  1 | 28 |
---------------------------------------------------------------------------------

Result Cache Information (identified by operation id):
------------------------------------------------------
3 - column-count=4; dependencies=(SH.SALES); name="RCMV_SALES"

18 rows selected.

キャッシュされた結果を作成する問合せを実行します。

SELECT channel_id, SUM(amount_sold)
FROM sales
GROUP BY channel_id;

CHANNEL_ID  SUM(AMOUNT_SOLD)
----------  ----------------
2        26346342.3
4          13706802
3        57875260.6
9         277426.26

結果キャッシュ内でマテリアライズド・ビューがマテリアライズされていることを確認します。

CONNECT / AS SYSDBA

SELECT name, scan_count hits, block_count blocks, depend_count dependencies
FROM V$RESULT_CACHE_OBJECTS
WHERE name = 'RCMV_SALES';

NAME         HITS    BLOCKS   DEPENDENCIES
----------   ----    ------   ------------
RCMV_SALES      0         5              1

最後に、RCMV問合せの同等化を削除します。

begin
sys.DBMS_ADVANCED_REWRITE.Drop_Rewrite_equivalence('RCMV_SALES');
end;
/

結果キャッシュの詳細は、『Oracle Databaseパフォーマンス・チューニング・ガイド』を参照してください。

クエリー・リライトが発生したことの確認

クエリー・リライトは透過的に行われるので、問合せがリライトされたかどうかを確認するには、特別なステップを実行する必要があります。問合せが高速に実行された場合、リライトが発生したと考えられますが、これは確認にはなりません。そのため、EXPLAIN PLAN文またはDBMS_MVIEW.EXPLAIN_REWRITEプロシージャを使用して、クエリー・リライトが発生したことを確認します。

クエリー・リライトでのEXPLAIN PLANの使用

EXPLAIN PLAN機能の使用方法については、『Oracle Database SQL言語リファレンス』を参照してください。クエリー・リライトの場合、チェックする必要があるのは、操作(OPERATION)にMAT_VIEW REWRITE ACCESSが示されていることのみです。示されている場合、クエリー・リライトが発生したということになります。次の例では、マテリアライズド・ビューcal_month_sales_mvを作成します。

CREATE MATERIALIZED VIEW cal_month_sales_mv
ENABLE QUERY REWRITE AS
SELECT  t.calendar_month_desc, SUM(s.amount_sold) AS dollars
FROM sales s, times t WHERE s.time_id = t.time_id
GROUP BY t.calendar_month_desc;

次のSQL文でEXPLAIN PLANが使用された場合、結果はデフォルトのPLAN_TABLE表に格納されます。ただし、PLAN_TABLEはutlxplan.sqlスクリプトを使用して最初に作成する必要があります。EXPLAIN PLANでは問合せが実際には実行されないことに注意してください。

EXPLAIN PLAN FOR
SELECT  t.calendar_month_desc, SUM(s.amount_sold)
FROM  sales s, times t WHERE s.time_id = t.time_id
GROUP BY t.calendar_month_desc;

PLAN_TABLEから得られる情報のうちクエリー・リライトに関して必要なものは、この問合せの実行方法を特定するための操作OBJECT_NAMEのみです。したがって次のように、出力には操作MAT_VIEW REWRITE ACCESSが表示されます。

SELECT OPERATION, OBJECT_NAME FROM PLAN_TABLE;

OPERATION                  OBJECT_NAME
--------------------       -----------------------
SELECT STATEMENT
MAT_VIEW REWRITE ACCESS    CALENDAR_MONTH_SALES_MV

クエリー・リライトでのEXPLAIN_REWRITEプロシージャの使用

問合せがリライトされない場合、その原因を容易には特定できないことがあります。クエリー・リライトの対象になるかどうかを制御するルールはきわめて複雑で、制約、ディメンション、クエリー・リライトの整合性モード、マテリアライズド・ビューが最新かどうか、問合せ自体のタイプなど、様々な要因が関係します。また、クエリー・リライトで特定のマテリアライズド・ビューが選択された理由も知る必要があります。そのため、問合せをリライトできる場合にそれを知らせ、リライトできない場合はその理由を知らせるDBMS_MVIEW.EXPLAIN_REWRITEプロシージャが用意されています。DBMS_MVIEW.EXPLAIN_REWRITEの結果を使用すると、問合せをできるかぎりリライトさせるために必要となる適切な措置を講じることができます。

EXPLAIN_REWRITE文で指定した問合せは、実際には実行されないことに注意してください。

DBMS_MVIEW.EXPLAIN_REWRITEの構文

DBMS_MVIEW.EXPLAIN_REWRITEからの出力を取得するには、2つの方法があります。1つは表を使用する方法、もう1つはVARRAYを作成する方法です。出力表を使用するための基本構文は、次のとおりです。

DBMS_MVIEW.EXPLAIN_REWRITE (
query           VARCHAR2,
mv              VARCHAR2(30),
statement_id    VARCHAR2(30));

スクリプトutlxrw.sqlを実行すると、出力表REWRITE_TABLEを作成できます。

queryパラメータは、SQL問合せを表すテキスト文字列です。mvパラメータには、schema.mvという形式の完全修飾されたマテリアライズド・ビュー名を指定します。これは、オプション・パラメータです。このパラメータを指定しない場合、EXPLAIN_REWRITEは、指定されたクエリー・リライトに使用できるすべてのマテリアライズド・ビューについて関連メッセージを戻します。schemaを省略してmvのみを指定すると、EXPLAIN_REWRITEでは現在のスキーマにあるマテリアライズド・ビューが検索されます。

EXPLAIN_REWRITEの出力を表ではなくVARRAYに送る場合は、このプロシージャを次のようにコールする必要があります。

DBMS_MVIEW.EXPLAIN_REWRITE (
query           [VARCHAR2 | CLOB],
mv               VARCHAR2(30),
output_array     SYS.RewriteArrayType);

問合せの長さが256文字未満であれば、EXPLAIN_REWRITEは、SQL*PlusのEXECUTEコマンドを使用して簡単に呼び出すことができます。256文字以上の場合は、PL/SQLのBEGIN... ENDブロックを/rdbms/demo/smxrw*の例のように使用する方法をお薦めします。

REWRITE_TABLEの使用

EXPLAIN_REWRITEの出力は、表REWRITE_TABLEに送ることができます。この出力表を作成するには、スクリプトutlxrw.sqlを実行します。このスクリプトはadminディレクトリにあります。REWRITE_TABLEの形式は、次のとおりです。

CREATE TABLE REWRITE_TABLE(
statement_id          VARCHAR2(30),   -- id for the query
mv_owner              VARCHAR2(30),   -- owner of the MV
mv_name               VARCHAR2(30),   -- name of the MV
sequence              INTEGER,        -- sequence no of the msg
query                 VARCHAR2(2000), -- user query
query_block_no        INTEGER,        -- block no of the current subquery
rewritten_txt         VARCHAR2(2000), -- rewritten query
message               VARCHAR2(512),  -- EXPLAIN_REWRITE msg
pass                  VARCHAR2(3),    -- rewrite pass no
mv_in_msg             VARCHAR2(30),   -- MV in current message
measure_in_msg        VARCHAR2(30),   -- Measure in current message
join_back_tbl         VARCHAR2(30),   -- Join back table in message
join_back_col         VARCHAR2(30),   -- Join back column in message
original_cost         INTEGER,        -- Cost of original query
rewritten_cost        INTEGER,        -- Cost of rewritten query
flags                 INTEGER,        -- associated flags
reserved1             INTEGER,        -- currently not used
reerved2              VARCHAR2(10))   -- currently not used;

例19-15 REWRITE_TABLEを使用したEXPLAIN_REWRITE

PL/SQLコールの例を次に示します。

EXECUTE DBMS_MVIEW.EXPLAIN_REWRITE -
('SELECT p.prod_name, SUM(amount_sold) ' || -
'FROM sales s, products p ' || -
'WHERE s.prod_id = p.prod_id ' || -
' AND prod_name > ''B%'' ' || -
' AND prod_name < ''C%'' ' || -
'GROUP BY prod_name', -
'TestXRW.PRODUCT_SALES_MV', -
'SH');

SELECT message FROM rewrite_table ORDER BY sequence;
MESSAGE
--------------------------------------------------------------------------------
QSM-01033: query rewritten with materialized view, PRODUCT_SALES_MV
1 row selected.

デモ・ファイルxrwutl.sqlには、EXPLAIN_REWRITEのより詳細な出力を表示するためのプロシージャが用意されています。詳細は、「EXPLAIN_REWRITEの出力」を参照してください。

次に、いくつかのマテリアライズド・ビューがある中で、結果的にsales_mvが最適なマテリアライズド・ビューとして選択された理由を、1つの具体例を基にして詳しく説明します。

DECLARE
qrytext VARCHAR2(500)  :='SELECT cust_first_name, cust_last_name,
SUM(amount_sold) AS dollar_sales FROM sales s, customers c WHERE s.cust_id=
c.cust_id GROUP BY cust_first_name, cust_last_name';
idno    VARCHAR2(30) :='ID1';
BEGIN
DBMS_MVIEW.EXPLAIN_REWRITE(qrytext, '', idno);
END;
/
SELECT message FROM rewrite_table ORDER BY sequence;

SQL> MESSAGE
--------------------------------------------------------------------------------
QSM-01082: Joining materialized view, CAL_MONTH_SALES_MV, with table, SALES, not possible
QSM-01022: a more optimal materialized view than PRODUCT_SALES_MV was used to rewrite
QSM-01022: a more optimal materialized view than FWEEK_PSCAT_SALES_MV was used to rewrite
QSM-01033: query rewritten with materialized view, SALES_MV

VARRAYの使用

EXPLAIN_REWRITEの出力はPL/SQLのVARRAYに保存できます。この配列の要素はRewriteMessage型で、SYSスキーマで次のように事前定義されています。

TYPE RewriteMessage IS OBJECT(
mv_owner        VARCHAR2(30),   -- MV's schema
mv_name         VARCHAR2(30),   -- Name of the MV
sequence        NUMBER(3),      -- sequence no of the msg
query_text      VARCHAR2(2000), -- User query
query_block_no  NUMBER(3),      -- block no of the current subquery
rewritten_text  VARCHAR2(2000), -- rewritten query text
message         VARCHAR2(512),  -- EXPLAIN_REWRITE error msg
pass            VARCHAR2(3),    -- Query rewrite pass no
mv_in_msg       VARCHAR2(30),   -- MV in current message
measure_in_msg  VARCHAR2(30),   -- Measure in current message
join_back_tbl   VARCHAR2(30),   -- Join back table in current msg
join_back_col   VARCHAR2(30),   -- Join back column in current msg
original_cost   NUMBER(10),     -- Cost of original query
rewritten_cost  NUMBER(10),     -- Cost rewritten query
flags           NUMBER,         -- Associated flags
reserved1       NUMBER,         -- For future use
reserved2       VARCHAR2(10)    -- For future use
);

配列型RewriteArrayTypeは、RewriteMessageオブジェクトのVARRAYで、SYSスキーマに次のように事前定義されています。

- TYPE RewriteArrayType AS VARRAY(256) OF RewriteMessage;

- この配列型を使用すると、配列変数を宣言して、それをEXPLAIN_REWRITE文の中に指定できます。

- 各RewriteMessageレコードには、リライト処理に関するメッセージがあります。

- パラメータはREWRITE_TABLEと同じですが、出力としてVARRAYを使用する場合、statement_idは使用しません。

- mv_ownerフィールドでは、メッセージに関連するマテリアライズド・ビューの所有者が定義されます。

- mv_nameフィールドでは、メッセージに関連するマテリアライズド・ビューの名前が定義されます。

- sequenceフィールドでは、メッセージに必要な順序が定義されます。

- query_textフィールドには、分析対象となる問合せテキストの最初の2000文字が含まれます。

- messageフィールドには、queryのリライト処理に関連するメッセージのテキストが含まれます。

- flags、reserved1およびreserved2フィールドは、将来のために予約されています。

例19-16 VARRAYを使用したEXPLAIN_REWRITE

次のマテリアライズド・ビューを考えてみます。

CREATE MATERIALIZED VIEW avg_sales_city_state_mv
ENABLE QUERY REWRITE AS
SELECT c.cust_city, c.cust_state_province, AVG(s.amount_sold)
FROM sales s, customers c WHERE s.cust_id = c.cust_id
GROUP BY c.cust_city, c.cust_state_province;

このマテリアライズド・ビューを次の問合せで使用してみます。

SELECT c.cust_state_province, AVG(s.amount_sold)
FROM sales s, customers c WHERE s.cust_id = c.cust_id
GROUP BY c.cust_state_province;

ただし、このマテリアライズド・ビューでは、問合せはリライトされません。これでは、リライトに必要な情報がすべてマテリアライズド・ビューにあるように思われるため、不慣れなユーザーは混乱しがちです。特定のマテリアライズド・ビューからAVGを計算できないことは、DBMS_MVIEW.EXPLAIN_REWRITEから判断できます。問題は、ここではROLLUPが必要であり、AVGではCOUNTまたはSUMのROLLUPが必要であることです。

前述の問合せのPL/SQLブロックの例は、出力としてVARRAYを使用すると次のようになります。

SET SERVEROUTPUT ON
DECLARE
Rewrite_Array SYS.RewriteArrayType := SYS.RewriteArrayType();
querytxt VARCHAR2(1500) := 'SELECT c.cust_state_province,
AVG(s.amount_sold)
FROM sales s, customers c WHERE s.cust_id = c.cust_id
GROUP BY c.cust_state_province';
i NUMBER;
BEGIN
DBMS_MVIEW.EXPLAIN_REWRITE(querytxt, 'AVG_SALES_CITY_STATE_MV',
Rewrite_Array);
FOR i IN 1..Rewrite_Array.count
LOOP
DBMS_OUTPUT.PUT_LINE(Rewrite_Array(i).message);
END LOOP;
END;
/

このEXPLAIN_REWRITE文の出力を次に示します。

QSM-01065: materialized view, AVG_SALES_CITY_STATE_MV, cannot compute
measure, AVG, in the query
QSM-01101: rollup(s) took place on mv, AVG_SALES_CITY_STATE_MV
QSM-01053: NORELY referential integrity constraint on table, CUSTOMERS,
in TRUSTED/STALE TOLERATED integrity mode
PL/SQL procedure successfully completed.

EXPLAIN_REWRITEのメリットに関する統計情報

EXPLAIN_REWRITEの出力には、original_costおよびrewritten_costという2つの列があり、クエリー・コストの見積りに役立ちます。original_cost列は、クエリー・リライトを使用禁止にした場合のクエリー・コストに関するオプティマイザの見積りを提供し、rewritten_cost列は、マテリアライズド・ビューを使用して問合せをリライトした場合の見積りを提供します。これらのコスト値は、特定の問合せがリライトから受けるメリットを調べる場合に使用できます。

EXPLAIN_REWRITEでの32KBを超える問合せテキストのサポート

このリリースでは、サイズの大きな問合せでも処理できるよう、EXPLAIN_REWRITEプロシージャが強化されました。入力問合せテキストをVARCHARデータ型のかわりにCLOBデータ型で定義できるようになりました。これにより、EXPLAIN_REWRITEで最大4GBの問合せが許容されます。

次に、CLOBを使用して表に出力する場合のEXPLAIN_REWRITEの構文を示します。

DBMS_MVIEW.EXPLAIN_REWRITE(
query          IN CLOB,
mv             IN VARCHAR2,
statement_id   IN VARCHAR2);

2番目の引数mvおよび3番目の引数statement_idには、NULLを指定できます。同様に、CLOBを使用してVARRAYに出力する場合のEXPLAIN_REWRITEの構文を示します。

DBMS_MVIEW.EXPLAIN_REWRITE(
query           IN CLOB,
mv              IN VARCHAR2,
msg_array       IN OUT SYS.RewriteArrayType);

表に出力する場合と同様、2番目の引数mvにはNULLを指定できます。CLOBでの長い問合せテキストは、DBMS_LOBパッケージのプロシージャを使用して生成できます。

EXPLAIN_REWRITEおよび複数のマテリアライズド・ビュー

複数のマテリアライズド・ビューを使用したEXPLAIN_REWRITEの構文は、単一のマテリアライズド・ビューを使用した場合の構文と同じです。ただし、複数のマテリアライズド・ビューはカンマで区切った文字列として指定します。たとえば、マテリアライズド・ビューの特定のセットmv1、mv2およびmv3を問合せquery_txtのリライトに使用できるかどうかを判断し、使用できない場合にその原因を調べるには、次のようにEXPLAIN_REWRITEを使用します。

DBMS_MVIEW.EXPLAIN_REWRITE(query_txt, 'mv1, mv2, mv3')

マテリアライズド・ビューの特定のセットを使用して問合せquery_txtがリライトされた場合、次のメッセージが表示されます。

QSM-01127: query rewritten with materialized view(s), mv1, mv2, and mv3.

1つ以上のマテリアライズド・ビューの特定のセットを使用したクエリー・リライトが失敗した場合、リライトに使用されなかった各マテリアライズド・ビューについてEXPLAIN_REWRITEによって失敗の原因が出力されます。

EXPLAIN_REWRITEの出力

/rdbms/demo/smxrw.sqlには、EXPLAIN_REWRITEの使用方法を示した具体例がいくつか用意されています。またデモのxrw領域には、SYS.XRWというユーティリティがあり、EXPLAIN_REWRITEプロシージャの出力内容の選択に使用できます。EXPLAIN_REWRITEにより問合せの評価が行われると、その出力には、リライトされた問合せテキスト、問合せのブロック番号、リライトされた問合せのコストなどの情報が反映されます。ユーティリティSYS.XRWでは、ユーザーにより指定されたフィールドが一定の書式に基づいて出力されるため、内容の判読が容易です。構文は次のとおりです。

SYS.XRW(list_of_mvs, list_of_commands, query_text),

ただし、list_of_mvsは、クエリー・リライトで使用されると予想されるマテリアライズド・ビューを表します。マテリアライズド・ビューが複数存在する場合は、それぞれをカンマで区切る必要があります。list_of_commandsは、次のいずれかのフィールドを表します。

QUERY_TXT:      User query text
REWRITTEN_TXT:  Rewritten query text
QUERY_BLOCK_NO: Query block number to identify each query blocks in
case the query has subqueries or inline views
PASS:           Pass indicates whether a given message was generated
before or after the view merging process of query rewrite.
COSTS:          Costs indicates the estimated execution cost of the
original query and the rewritten query

次の例は、このユーティリティの使用方法を示したものです。

DROP MATERIALIZED VIEW month_sales_mv;

CREATE MATERIALIZED VIEW month_sales_mv
ENABLE QUERY REWRITE
AS
SELECT t.calendar_month_number, SUM(s.amount_sold) AS sum_dollars
FROM sales s, times t
WHERE s.time_id = t.time_id
GROUP BY t.calendar_month_number;

SET SERVEROUTPUT ON
DECLARE
querytxt VARCHAR2(1500) := 'SELECT t.calendar_month_number,
SUM(s.amount_sold) AS sum_dollars FROM sales s, times t
WHERE s.time_id = t.time_id GROUP BY t.calendar_month_number';
BEGIN
SYS.XRW('MONTH_SALES_MV', 'COSTS, PASS, REWRITTEN_TXT, QUERY_BLOCK_NO', querytxt);
END;
/

SYS.XRWの出力は次のようになります。SYS.XRWの出力には、リライト前およびリライト後の問合せのコスト、リライトされた問合せテキスト、問合せのブロック番号、およびビューのマージ処理の前後でメッセージが生成されたかどうかが反映されていることがわかります。

============================================================================
>> MESSAGE  : QSM-01151: query was rewritten
>> RW QUERY : SELECT MONTH_SALES_MV.CALENDAR_MONTH_NUMBER CALENDAR_MONTH_NUMBER,
MONTH_SALES_MV.SUM_DOLLARS SUM_DOLLARS FROM SH.MONTH_SALES_MV MONTH_SALES_MV
>> ORIG COST: 19.952763130792                  RW COST: 1.80687108
============================================================================
>>
------------------------- ANALYSIS OF QUERY REWRITE -------------------------
>>
>> QRY BLK #: 0
>> MESSAGE  : QSM-01209: query rewritten with materialized view,
MONTH_SALES_MV, using text match algorithm
>> RW QUERY : SELECT MONTH_SALES_MV.CALENDAR_MONTH_NUMBER CALENDAR_MONTH_NUMBER,
MONTH_SALES_MV.SUM_DOLLARS SUM_DOLLARS FROM SH.MONTH_SALES_MV MONTH_SALES_MV
>> ORIG COST: 19.952763130792                  RW COST: 1.80687108
>> MESSAGE OUTPUT BEFORE VIEW MERGING...
============================ END OF MESSAGES ===============================
PL/SQL procedure successfully completed.

クエリー・リライトを改善するための設計上の考慮事項

この項では、クエリー・リライトのメリットを最大限引き出す上で役立つ、設計上の考慮事項について説明します。これらは、クエリー・リライトを使用するために必須ではありません。また、これらのガイドラインに従っても、リライトが保証されるわけではありません。考慮する必要がある一般的なルールは次のとおりです。

- クエリー・リライトの考慮事項: 制約

- クエリー・リライトの考慮事項: ディメンション

- クエリー・リライトの考慮事項: 外部結合

- クエリー・リライトの考慮事項: テキストの一致

- クエリー・リライトの考慮事項: 集計

- クエリー・リライトの考慮事項: グルーピング条件

- クエリー・リライトの考慮事項: 式の一致

- クエリー・リライトの考慮事項: デート・フォールディング

- クエリー・リライトの考慮事項: 統計情報

- クエリー・リライトの考慮事項: ヒント

クエリー・リライトの考慮事項: 制約

マテリアライズド・ビューで参照されるすべての内部結合に、外部キー列に対する追加のNOT NULL制約を使用した参照整合性(外部キー/主キー制約)があることを確認します。制約は通常、大量のオーバーヘッドを伴うため、それらをNO VALIDATEおよびRELYにして、パラメータQUERY_REWRITE_INTEGRITYをSTALE_TOLERATEDまたはTRUSTEDに設定することも可能です。ただし、QUERY_REWRITE_INTEGRITYをENFORCEDに設定した場合、リライト機能の効果を最大限に高めるには、すべての制約をENABLED状態、ENFORCED状態およびVALIDATED状態に設定する必要があります。

予期しない結果となる可能性があるため、ON DELETE句は使用しないようにする必要があります。

クエリー・リライトの考慮事項: ディメンション

正規化されたディメンション表または非正規化ディメンション表の階層関係および機能依存性は、ディメンションのHIERARCHY句およびDETERMINES句を使用して表現できます。ディメンションは、制約では表現できない表内関係を表現できます。ディメンションで宣言された関係をクエリー・リライトで利用するには、パラメータQUERY_REWRITE_INTEGRITYをTRUSTEDまたはSTALE_TOLERATEDに設定する必要があります。

クエリー・リライトの考慮事項: 外部結合

制約を回避するもう1つの方法は、マテリアライズド・ビューに外部結合を使用する方法です。クエリー・リライトは、BのROWIDまたは列B.bがマテリアライズド・ビュー内で使用可能な場合、(A.a=B.b)などの問合せ内の内部結合を、マテリアライズド・ビュー(A.a = B.b(+))内の外部結合から導出できます。外部結合を使用したリライトのほとんどは、結合のみを使用したマテリアライズド・ビューによってサポートされます。それを活用するためには、外部結合を使用したマテリアライズド・ビューは、外部結合の内部表のROWIDまたは主キーを格納する必要があります。たとえば、マテリアライズド・ビューjoin_sales_time_product_mv_ojには、外部結合の内部表の主キーprod_idおよびtime_idが格納されます。

クエリー・リライトの考慮事項: テキストの一致

極端に複雑で実行に時間がかかる問合せについて、その処理時間を短縮する必要がある場合は、問合せと同じテキストを使用したマテリアライズド・ビューを作成します。マテリアライズド・ビューには問合せの結果が格納されるため、複合結合の実行に要する時間や全データの中から必要なデータを検索するのに要する時間を節約できます。

クエリー・リライトの考慮事項: 集計

クエリー・リライトで最大の効果を得ることができるよう、問合せの集計を計算するために必要なすべての集計がマテリアライズド・ビューに存在していることを確認します。集計の条件は、増分リフレッシュの条件とよく似ています。たとえば、AVG(x)が問合せ内にある場合、COUNT(x)およびAVG(x)、またはSUM(x)およびCOUNT(x)をマテリアライズド・ビューに格納する必要があります。高速リフレッシュの要件については、「高速リフレッシュにおける一般的な制限」を参照してください。

クエリー・リライトの考慮事項: グルーピング条件

階層のより低いレベルでデータを集計すると、より高いレベルでデータを集計するより効率的です。より低いレベルは、より多くのクエリー・リライトに使用されるためです。ただし、それによって、より多くの領域が必要となることに注意してください。たとえば、州でグルーピングするのではなく、市でグルーピングした場合などです(領域の制約で禁止されていない場合)。

オーバーラップした、または階層的に関係付けられたGROUP BY列を使用した複数のマテリアライズド・ビューを作成するかわりに、それらすべてのGROUP BY列を使用した単一のマテリアライズド・ビューを作成します。たとえば、市でグルーピングされたマテリアライズド・ビュー、および月でグルーピングされた別のマテリアライズド・ビューを使用するかわりに、市および月でグルーピングされた1つのマテリアライズド・ビューを使用します。

ディメンション内のレベルに対応する列にGROUP BYを使用し、機能的に依存している列には使用しません。これは、クエリー・リライトは、ディメンション内のDETERMINES句をベースにして、機能依存性を自動的に使用できるためです。たとえば、prod_nameでグルーピングするかわりに、prod_idでグルーピングします(属性prod_idによりprod_nameが決定されることを示すディメンションがあれば、prod_nameに関するクエリー・リライトを使用可能にできます)。

クエリー・リライトの考慮事項: 式の一致

複数の問合せに共通の副次選択がある場合、SELECT列の1つとして共通の副次選択を使用したマテリアライズド・ビューを作成すると効果的です。その場合、共通の副次選択の事前計算によって、複数の問合せにおけるパフォーマンスが向上します。

クエリー・リライトの考慮事項: デート・フォールディング

月、四半期、年などのフォールドされた日付単位ごとにデータを集計するマテリアライズド・ビューを作成する場合、年コンポーネントは、常に接頭辞として使用し、接尾辞としては使用しません。たとえば、TO_CHAR(date_col, 'yyyy-q')は、日付を四半期にフォールドし、年の順序にそろえますが、TO_CHAR(date_col, 'q-yyyy')は、日付を四半期にフォールドし、四半期の順序にそろえます。前者は順序を維持しますが、後者は順序を維持しません。このため、年の接頭辞なしで作成されたすべてのマテリアライズド・ビューは、デート・フォールディングのリライトには使用できません。

クエリー・リライトの考慮事項: 統計情報

マテリアライズド・ビューを使用した最適化は、コストベースで実行されます。オプティマイザがコストベースの選択をするには、マテリアライズド・ビューと問合せの表の両方の統計情報が必要です。そのため、マテリアライズド・ビューにはDBMS_STATSパッケージを使用して収集された情報が必要です。

クエリー・リライトの考慮事項: ヒント

この項の内容は、次のとおりです。

- REWRITEヒントおよびNOREWRITEヒント

- REWRITE_OR_ERRORヒント

- 複数のマテリアライズド・ビューでのリライトのヒント

- EXPAND_GSET_TO_UNIONヒント

REWRITEヒントおよびNOREWRITEヒント

SQL文のSELECTブロックにヒントを含めると、クエリー・リライトの発生を制御できます。問合せにNOREWRITEヒントを使用すると、オプティマイザが問合せをリライトすることを回避できます。

問合せに引数を持たないREWRITEヒントを使用すると、オプティマイザでは、コストにかかわらずマテリアライズド・ビュー(ある場合)によるリライトが強制的に実行されます。引数を持つREWRITE(mv1,mv2,...)ヒントを使用すると、指定した名前のリストから最適なマテリアライズド・ビューを選択してリライトを強制できます。

リライトを防止するには、次の文を使用できます。

SELECT /*+ NOREWRITE */ p.prod_subcategory, SUM(s.amount_sold)
FROM   sales s, products p WHERE  s.prod_id = p.prod_id
GROUP BY p.prod_subcategory;

sum_sales_pscat_week_mvを使用してリライトを強制するには(リライトが可能な場合)、次の文を使用します。

SELECT /*+ REWRITE (sum_sales_pscat_week_mv) */
p.prod_subcategory,  SUM(s.amount_sold)
FROM   sales s, products p WHERE  s.prod_id=p.prod_id
GROUP BY p.prod_subcategory;

リライト・ヒントの有効範囲は問合せブロックです。SQL文が複数の問合せブロック(SELECT句)からなる場合、文全体のリライトを制御するには、各問合せブロックにリライト・ヒントを指定する必要があります。

REWRITE_OR_ERRORヒント

問合せでREWRITE_OR_ERRORヒントを使用すると、問合せがリライトに失敗した場合、次のエラーが発生します。

ORA-30393: a query block in the statement did not rewrite

たとえば、次の問合せでは、使用するクエリー・リライトに適切なマテリアライズド・ビューがない場合、ORA-30393エラーを発行します。

SELECT /*+ REWRITE_OR_ERROR */ p.prod_subcategory, SUM(s.amount_sold)
FROM sales s, products p WHERE s.prod_id = p.prod_id
GROUP BY p.prod_subcategory;

複数のマテリアライズド・ビューでのリライトのヒント

複数のマテリアライズド・ビューを使用する際にリライトを制御するヒントが2つあります。NO_MULTIMV_REWRITEヒントを使用すると、2つ以上のマテリアライズド・ビューによる問合せのリライトが行われなくなり、NO_BASETABLE_MULTIMV_REWRITEヒントを使用すると、マテリアライズド・ビューと実表の組合せによる問合せのリライトが行われなくなります。

EXPAND_GSET_TO_UNIONヒント

EXPAND_GSET_TO_UNIONヒントを使用すると、GROUP BY拡張機能を持つ問合せを、強制的に等価なUNION ALL問合せに拡張できます。詳細は、「拡張GROUP BYを持つ問合せのヒント」を参照してください。

20 スキーマのモデリング化技法

この章では、データ・ウェアハウスのスキーマについて説明します。内容は次のとおりです。

- データ・ウェアハウスのスキーマ

- 第3正規形

- スター・スキーマ

- スター・クエリーの最適化

データ・ウェアハウスのスキーマ

スキーマとは、表、ビュー、索引およびシノニムを含むデータベース・オブジェクトのコレクションです。

データ・ウェアハウス用に設計されたスキーマ・モデルにスキーマ・オブジェクトを配置するためには、様々な方法があります。データ・ウェアハウス・スキーマ・モデルの1つは、スター・スキーマです。shサンプル・スキーマ(このマニュアルに記載するほとんどの例の基本)では、スター・スキーマを使用します。ただし、その他にも、データ・ウェアハウスによく使用されるスキーマ・モデルがあります。そのようなスキーマ・モデルのうち最も一般的なのは、第3正規形(3NF)スキーマです。また、スター・スキーマでも3NFスキーマでもなく、かわりにこの2つのスキーマの特性を兼ね備えたハイブリッド・スキーマ・モデルと呼ばれるスキーマもあります。

Oracle Databaseは、すべてのデータ・ウェアハウス・スキーマをサポートするよう設計されています。機能によっては、1つのスキーマ・モデルに固有のものもあります(たとえば、スター変換機能はスター・スキーマに固有です。これは、「スター型変換の使用」に記載されています)。ただし、Oracleのデータ・ウェアハウス機能の大多数は、スター・スキーマ、3NFスキーマおよびハイブリッド・スキーマに同じように適用できます。パーティション化(ローリング・ウィンドウのロード手法を含む)、パラレル化、マテリアライズド・ビュー、分析SQLなどの主なデータ・ウェアハウス機能は、すべてのスキーマ・モデルに実装されています。

データ・ウェアハウスにどのスキーマ・モデルを使用するかは、データ・ウェアハウス・プロジェクト・チームの要件と選択に基づいて決定されます。代替スキーマ・モデルの利点の比較は、本書の範囲外です。かわりに、この章では、各スキーマ・モデルを簡単に紹介し、それらの環境に対してOracle Databaseを最適化する方法を提示します。

第3正規形

本書では、例として主にスター・スキーマを使用していますが、データ・ウェアハウスの実装に第3正規形を使用することもできます。

第3正規形モデルは、正規化を介してデータの冗長性を最小限に抑える古典的なリレーショナル・データベース・モデリング手法です。3NFスキーマでは通常、スター・スキーマと比べると、正規化処理のために表の数が多くなります。たとえば、図20-1で、orders表およびorder items表には、図20-2のスター・スキーマのsales表と同じ情報が含まれています。

通常、3NFスキーマは、大規模なデータ・ウェアハウス、特に、データのロード要求が多く、データ・マートへのデータの入力および長時間実行問合せの実行に使用される環境用として選択されます。

3NFスキーマの主なメリットは、次のとおりです。

- アプリケーションやデータの使用方法の考慮事項とは独立した、中立的なスキーマ設計を提供します。

- スター・スキーマのように正規化度の高いスキーマに比べて、データ変換が少なくて済みます。

図20-1に、第3正規形を示します。

図20-1 第3正規形

第3正規形の問合せの最適化

3NFスキーマでは、多くの場合、問合せが非常に複雑になり、多数の表を必要とします。このため、大規模な表の結合のパフォーマンスが、3NFスキーマを使用する際の主な考慮事項になります。

3NFスキーマの特に重要な機能の1つは、パーティション・ワイズ結合です。3NFスキーマの最大の表は、パーティション・ワイズ結合を使用可能にするためにパーティション化する必要があります。こうした環境で最も一般的なパーティション化手法は、最大の表に対するコンポジット・レンジ-ハッシュ・パーティション化であり、最も一般的な結合キーがハッシュ・パーティション化キーとして選択されます。

3NF環境では、パラレル化が非常によく利用されるため、通常はパラレル化を使用可能にする必要があります。

スター・スキーマ

スター・スキーマは、おそらく最も単純なデータ・ウェアハウス・スキーマです。スター・スキーマをエンティティ関連図で表すと、星(スター)のように中央の表から点が放射状に広がっているため、スター・スキーマと呼ばれます。スターの中心は1つの大規模なファクト表で構成されており、スターの先端はディメンション表になっています。

スター・クエリーは、ファクト表といくつかのディメンション表を結合するものです。各ディメンション表は、主キーから外部キーへの結合を使用してファクト表に結合されますが、ディメンション表同士は結合されません。オプティマイザによってスター・クエリーが認識されると、スター・クエリーのための効率的な実行計画が生成されます。スター型変換を有効化するにあたり、ファクト表の外部キーは必須ではありません。

一般的なファクト表には、キーおよびメジャーが含まれます。たとえばshサンプル・スキーマの場合、ファクト表salesには、メジャーとしてquantity_sold、amountおよびcostが含まれ、キーとしてcust_id、time_id、prod_id、channel_idおよびpromo_idが含まれます。ディメンション表は、customers、times、products、channelsおよびpromotionsです。たとえば、productsディメンション表には、ファクト表に表示される各製品番号に関する情報があります。

スター型結合とは、ディメンション表とファクト表の主キーと外部キーの結合です。

スター・スキーマの主なメリットは、次のとおりです。

- エンド・ユーザーによって分析されるビジネス・エンティティとスキーマ・デザイン間の直接的および直感的マッピングを提供します。

- 一般的なスター・クエリーに、高度に最適化されたパフォーマンスを提供します。

- 多数のビジネス・インテリジェンス・ツールで幅広くサポートされます。これらのツールは、データ・ウェアハウス・スキーマがディメンション表を含むことを想定または必須要件としている場合があります。

スター・スキーマは、単純なデータ・マートと非常に大規模なデータ・ウェアハウスの両方に使用されます。

図20-2に、スター・スキーマを示します。

図20-2 スター・スキーマ

スノーフレーク・スキーマ

スノーフレーク・スキーマは、スター・スキーマより複雑なデータ・ウェアハウス・モデルであり、スター・スキーマの一種です。このスキーマの図がスノーフレーク(雪片)に似ているため、スノーフレーク・スキーマと呼ばれます。

スノーフレーク・スキーマでは、ディメンションが正規化され、冗長性が排除されます。つまり、ディメンション・データは1つの大規模な表ではなく複数の表にグルーピングされます。たとえば、スター・スキーマのproductディメンション表は、スノーフレーク・スキーマのproducts表、product_category表およびproduct_manufacturer表に正規化できます。これによって、領域が節約されますが、ディメンション表の数が増加し、より多くの外部キー結合が必要になります。その結果、問合せがより複雑になり、問合せのパフォーマンスが低下します。図20-3に、スノーフレーク・スキーマを示します。

図20-3 スノーフレーク・スキーマ

注意:
特に理由がなければ、スノーフレーク・スキーマではなくスター・スキーマを選択することをお薦めします。

スター・クエリーの最適化

スター・クエリーを使用する場合は、次の点を考慮する必要があります。

- スター・クエリーのチューニング

- スター型変換の使用

スター・クエリーのチューニング

スター・クエリーのパフォーマンスを最大限に向上させるためには、次の基本的なガイドラインに従う必要があります。

- ビットマップ索引をファクト表の各外部キー列上に作成する必要があります。

- 初期化パラメータSTAR_TRANSFORMATION_ENABLEDをTRUEに設定する必要があります。これにより、スター・クエリーのための重要なオプティマイザ機能が使用可能になります。この機能は、下位互換性のためにデフォルトでFALSEに設定されています。

データ・ウェアハウスがこれらの条件を満たす場合、そのデータ・ウェアハウスで実行しているほとんどのスター・クエリーは、スター型変換と呼ばれる問合せ実行計画を使用します。スター型変換によって、スター・クエリーの問合せパフォーマンスが向上します。

スター型変換の使用

スター型変換は、元のスター・クエリーのSQLを暗黙的にリライト(または変換)することによる、強力な最適化テクニックです。エンド・ユーザーがスター型変換の詳細を知る必要はありません。Oracle Databaseの問合せオプティマイザでは、該当する場合にスター型変換が自動的に選択されます。

スター型変換は、スター・クエリーを効率的に実行することを目的とした問合せ変換です。Oracle Databaseでは、2つの基本フェーズを使用してスター・クエリーが処理されます。第1フェーズでは、ファクト表から必要な行(結果セット)のみを取り出します。この取出しにはビットマップ索引が使用されるため、非常に効率的です。第2フェーズでは、この結果セットをディメンション表に結合します。たとえば、「西部および南西部販売地域における過去3四半期の食料品部門の売上および利益はどうであったか」というエンド・ユーザーの問合せがあるとします。これは、1つの単純なスター・クエリーです。

ビットマップ索引を使用したスター型変換

スター型変換の前提条件は、ファクト表の各結合列に単一列のビットマップ索引が存在することです。これらの結合列には、すべての外部キー列が含まれます。

たとえば、shサンプル・スキーマのsales表の場合、time_id列、channel_id列、cust_id列、prod_id列およびpromo_id列にビットマップ索引が定義されています。

次のスター・クエリーを考えてみます。

SELECT ch.channel_class, c.cust_city, t.calendar_quarter_desc,
SUM(s.amount_sold) sales_amount
FROM sales s, times t, customers c, channels ch
WHERE s.time_id = t.time_id
AND   s.cust_id = c.cust_id
AND   s.channel_id = ch.channel_id
AND   c.cust_state_province = 'CA'
AND   ch.channel_desc in ('Internet','Catalog')
AND   t.calendar_quarter_desc IN ('1999-Q1','1999-Q2')
GROUP BY ch.channel_class, c.cust_city, t.calendar_quarter_desc;

この問合せは、2つのフェーズで処理されます。第1フェーズでは、Oracle Databaseは、ファクト表の外部キー列上のビットマップ索引を使用して、ファクト表から必要な行のみを識別し、取り出します。つまり、Oracle Databaseは、主に次の問合せを使用して、ファクト表から結果セットを取り出します。

SELECT ... FROM sales
WHERE time_id IN
(SELECT time_id FROM times
WHERE calendar_quarter_desc IN('1999-Q1','1999-Q2'))
AND cust_id IN
(SELECT cust_id FROM customers WHERE cust_state_province='CA')
AND channel_id IN
(SELECT channel_id FROM channels WHERE channel_desc IN('Internet','Catalog'));

元のスター・クエリーがこの副問合せ表現に変換されたことから、これがアルゴリズムの変換ステップになります。ファクト表にアクセスするこの方法は、ビットマップ索引の効果を高めます。ビットマップ索引は、リレーショナル・データベース内に集合ベースの処理方法を提供します。Oracleでは、AND(標準的な集合についての用語で共通部分の意味)、OR(集合の用語で和集合)、MINUS、COUNTなどの集合演算を実行するための非常に高速な方法が実装されています。

このスター・クエリーでは、time_idのビットマップ索引を使用して、1999年第1四半期(1999-Q1)の売上(sales)に対応するファクト表のすべての行集合が識別されます。この集合は、ビットマップ(ファクト表のどの行がこの集合のメンバーであるかを示す1および0(ゼロ)の文字列)として表されます。

同様のビットマップが、1999年第2四半期(1999-Q2)の売上に対応するファクト表の行に取り出されます。ビットマップOR演算を使用して、このQ1の売上集合をQ2の売上集合と組み合せます。

追加の集合演算が、customerディメンションおよびproductディメンションに対して実行されます。この時点で、スター・クエリー処理には3つのビットマップがあります。各ビットマップは、別々のディメンション表に対応し、それぞれ、個々のディメンションの絞込み条件を満たすファクト表の行集合を表します。

これらの3つのビットマップは、ビットマップAND演算を使用して単一ビットマップに結合されます。この最後のビットマップは、ファクト表のうちディメンション表上の絞込み条件をすべて満たす行集合を表します。これは結果セットであり、問合せの評価に必要なファクト表からの正確な行集合です。ここまでのところでは、ファクト表の実際のデータには、まだアクセスしていないことに注意してください。これらの演算はすべて、ビットマップ索引とディメンション表のみを基にしています。ビットマップ索引はデータを圧縮した形で表すため、ビットマップの集合ベース演算は非常に効率的です。

結果セットが識別されると、ビットマップはSALES表から実データへのアクセスに使用されます。エンド・ユーザーの問合せに必要な行のみが、ファクト表から取り出されます。この時点で、すべてのディメンション表が、ビットマップ索引を使用してファクト表に効率的に結合されています。Oracle Databaseでは、各ディメンション表をファクト表に個別に結合するのではなく、すべてのディメンション表を単一の論理結合演算でファクト表に結合しているため、この技法を使用すると優れたパフォーマンスが得られます。

この問合せの第2フェーズでは、ファクト表の行(結果セット)をディメンション表に結合します。Oracleは、最も効率的な方法を使用して、ディメンション表にアクセスおよび結合します。ほとんどのディメンションは非常に小規模なため、これらのディメンション表への最も効率的なアクセス方法は、通常、表スキャンです。大規模なディメンション表については、表スキャンは最も効率的なアクセス方法ではない場合があります。前述の例では、product.departmentのビットマップ索引を使用して、食料品部門のすべての製品が高速で識別されます。Oracle Databaseでは、各ディメンション表のサイズおよびデータ分散に関するオプティマイザの知識に基づいて、オプティマイザが、特定のディメンション表に最適のアクセス方法を自動的に判断します。

同様に、各ディメンション表用の特定の結合方法(および索引付け方法)も、オプティマイザによってインテリジェントに判断されます。多くの場合、ディメンション表を結合するための最も効率的なアルゴリズムはハッシュ結合です。すべてのディメンション表が結合されると、最終結果がユーザーに戻されます。1つの表から一致する行のみを取り出してから、別の表に結合する問合せテクニックは、一般にセミ結合と呼ばれます。

ビットマップ索引を使用したスター型変換の実行計画

「ビットマップ索引を使用したスター型変換」から得られる典型的な実行計画は次のようになります。

SELECT STATEMENT
SORT GROUP BY
HASH JOIN
TABLE ACCESS FULL                          CHANNELS
HASH JOIN
TABLE ACCESS FULL                         CUSTOMERS
HASH JOIN
TABLE ACCESS FULL                        TIMES
PARTITION RANGE ITERATOR
TABLE ACCESS BY LOCAL INDEX ROWID       SALES
BITMAP CONVERSION TO ROWIDS
BITMAP AND
BITMAP MERGE
BITMAP KEY ITERATION
BUFFER SORT
TABLE ACCESS FULL                 CUSTOMERS
BITMAP INDEX RANGE SCAN            SALES_CUST_BIX
BITMAP MERGE
BITMAP KEY ITERATION
BUFFER SORT
TABLE ACCESS FULL                 CHANNELS
BITMAP INDEX RANGE SCAN            SALES_CHANNEL_BIX
BITMAP MERGE
BITMAP KEY ITERATION
BUFFER SORT
TABLE ACCESS FULL                 TIMES
BITMAP INDEX RANGE SCAN            SALES_TIME_BIX

この計画では、ファクト表は、ビットマップ・アクセス・パスを介してアクセスされます。このパスは、3つのマージされたビットマップのビットマップANDに基づきます。この3つのビットマップは、下位の行ソース・ツリーからビットマップが提供されているBITMAP MERGE行ソースによって生成されます。このような行ソース・ツリーはそれぞれ、副問合せ行ソース・ツリーの値をフェッチするBITMAP KEY ITERATION行ソースで構成されています。この例では、副問合せ行ソース・ツリーは、1つの全表アクセスです。これらの各値について、BITMAP KEY ITERATION行ソースがビットマップをビットマップ索引から取り出します。関係するファクト表の行は、このアクセス・パスを使用して取り出された後に、問合せ結果を生成するためにディメンション表および一時表と結合されます。

ビットマップ結合索引を使用したスター型変換

スター型変換において、ビットマップ索引の他にビットマップ結合索引も使用できます。次に示すような追加の索引構造があるとします。

CREATE BITMAP INDEX sales_c_state_bjix
ON sales(customers.cust_state_province)
FROM sales, customers
WHERE sales.cust_id = customers.cust_id
LOCAL NOLOGGING COMPUTE STATISTICS;

同じスター・クエリーでビットマップ結合索引を使用した場合の処理は、前述の例に似ています。唯一の違いは、単一表のビットマップ索引のかわりに、結合インデックスを使用してスター・クエリーの第1フェーズで顧客データにアクセスすることです。

ビットマップ結合索引を使用したスター型変換の実行計画

「ビットマップ結合索引を使用したスター型変換の実行計画」から得られる典型的な実行計画は次のようになります。

SELECT STATEMENT
SORT GROUP BY
HASH JOIN
TABLE ACCESS FULL                          CHANNELS
HASH JOIN
TABLE ACCESS FULL                         CUSTOMERS
HASH JOIN
TABLE ACCESS FULL                        TIMES
PARTITION RANGE ALL
TABLE ACCESS BY LOCAL INDEX ROWID       SALES
BITMAP CONVERSION TO ROWIDS
BITMAP AND
BITMAP INDEX SINGLE VALUE            SALES_C_STATE_BJIX
BITMAP MERGE
BITMAP KEY ITERATION
BUFFER SORT
TABLE ACCESS FULL                 CHANNELS
BITMAP INDEX RANGE SCAN            SALES_CHANNEL_BIX
BITMAP MERGE
BITMAP KEY ITERATION
BUFFER SORT
TABLE ACCESS FULL                 TIMES
BITMAP INDEX RANGE SCAN            SALES_TIME_BIX

この計画と前述の計画との唯一の違いは、customerディメンションのビットマップ索引スキャン内の選択のための処理がないことです。これは、customer.cust_state_provinceの結合述語情報を、ビットマップ結合索引sales_c_state_bjixで満たすことができるためです。

Oracleによるスター型変換の使用の選択

オプティマイザは、変換なしでも生成できる最適な計画を生成して保存します。変換が有効な場合、オプティマイザは問合せを変換し、必要に応じて、その変換された問合せを使用して最適な計画を生成します。オプティマイザは、この2つの問合せに対する最適な計画のコスト概算を比較して、変換または未変換のどちらの最適な計画を使用するかを決定します。

問合せがファクト表の行の大部分にアクセスする必要があるときは、変換ではなく、全表スキャンを使用する方がよい場合があります。ただし、ディメンション表について、選択による絞込み度合いが高く、ファクト表のわずかな部分のみを取り出す必要がある場合は、変換に基づく計画の方が適していることもあります。

オプティマイザは、多くの基準に基づいて適切であると判断した場合にのみ、ディメンション表に対して副問合せを生成します。副問合せは、すべてのディメンション表に対して生成されるわけではありません。また、オプティマイザが、表および問合せの性質に基づいて、問合せに変換を適用するメリットがないと判断することもあります。このような場合は、最適な通常の計画が使用されます。

スター型変換の制限

スター型変換は、次の特性が1つでもある表ではサポートされません。

- ビットマップ・アクセス・パスと非互換の表ヒントがある問合せ。

- バインド変数を含む問合せ。

- ビットマップ索引が少なすぎる表。オプティマイザが副問合せを生成するためには、ファクト表の列にビットマップ索引がある必要があります。

- リモート・ファクト表。ただし、リモート・ディメンション表は、生成された副問合せでは有効です。

- アンチ結合された表。

- 副問合せでディメンション表として使用済の表。

- ビュー・パーティションではなく、実際はマージされていないビューである表。

- ファクト表がマージされていないビューである表。

- パーティション・ビューがファクト表として使用されている表。

次の場合には、オプティマイザではスター型変換が選択されないことがあります。

- 効率的な単一表アクセス・パスを持つ表

- 小さすぎて変換によるメリットがない表

さらに、次の条件下では、スター型変換で一時表は使用されません。

- データベースが読取り専用モードの場合

- スター・クエリーがシリアル化可能モードでのトランザクションの一部である場合

21 データ・ウェアハウスにおける集計のためのSQL

この章では、データ・ウェアハウスの基本的な側面であるSQLによる集計処理について説明します。内容は次のとおりです。

- データ・ウェアハウスにおける集計SQLの概要

- ROLLUP(GROUP BYの拡張)

- CUBE(GROUP BYの拡張)

- GROUPING関数

- GROUPING SETS式

- 複合列

- 連結グルーピング

- 集計を使用する場合の考慮点

- WITH句を使用した計算

- SQLでの階層的キューブの処理

データ・ウェアハウスにおける集計SQLの概要

集計は、データ・ウェアハウスの基本的な処理です。ウェアハウスにおける集計パフォーマンスを向上させるため、Oracle Databaseでは次の機能が用意されています。

- GROUP BY句を拡張するCUBEおよびROLLUP

- 3つのGROUPING関数

- GROUPING SETS式

- ピボット操作

SQLに対するCUBE、ROLLUPおよびGROUPING SETS拡張により、問合せとレポートがより簡単で高速になります。CUBE、ROLLUPおよびグルーピング・セットでは、行を様々にグルーピングした文のUNION ALLと同じ単一の結果セットが生成されます。ROLLUPでは、SUM、COUNT、MAX、MINおよびAVGなどの集計を、最も詳細なものから総計まで、レベルを上げながら集計が作成されます。CUBEはROLLUPと同様の拡張で、一文で集計可能なすべての組合せについて計算を行うことができます。CUBE、ROLLUPおよびGROUPING SETS拡張を使用すると、GROUP BY句で必要なグルーピングを指定できます。これにより、CUBE操作を実行せずに複数のディメンション間で効率的に分析できます。CUBEの計算は、大きな処理負荷が生じますが、キューブをグルーピング・セットに置き換えると、パフォーマンスを大幅に改善できます。

パフォーマンスを向上させるために、CUBE、ROLLUPおよびGROUPING SETSをパラレル化できます。つまり、複数のプロセスで、すべての文を同時に実行できます。これらの機能によって集計計算がより効率的になるため、データベースのパフォーマンスおよびスケーラビリティが向上します。

3つのGROUPING関数を使用すると、各行が属するグループを識別し、小計行をソートして結果にフィルタを適用できます。

複数ディメンション間の分析

意思決定支援システムの重要な概念の1つは、多次元分析です。必要なディメンションをすべて組み合せて企業を調査します。ここでは、質問の指定に使用される任意のカテゴリという意味でディメンションという用語を使用します。最も一般的には、時間、地理、製品、部門、流通チャネルなどのディメンションが指定されますが、企業活動が多方面にわたるのと同様に、可能なディメンションの数にも制限はありません。特定のディメンション値の集合に対応付けられたイベントまたはエンティティは、通常、ファクトと呼ばれます。ファクトには、売上件数または国内通貨での売上金額、利益、顧客数、生産量など、追跡する価値があるすべてのものが含まれます。

多次元的な問合せの例を次に示します。

- 1999年および2000年について、すべての製品の総売上を、州から国、地域単位へと地理ディメンションの集計レベルを上げながら表示します。

- 1999年と2000年における南アメリカの地域別経費を示す事業のクロス集計分析を作成します。可能な小計をすべて組み込みます。

- 自動車製品に関する2000年の販売収入に従って、アジアでの販売代理店の上位10社をリストし、そのコミッションのランキングを作成します。

これらの問合せには、すべて複数のディメンションが伴います。多次元の質問の多くには、集計データの他、時間、地理または予算別のデータセットの比較が必要です。

アナリストは一般に、多数のディメンションを持つデータを視覚的に表現する際、データ・キューブ(n個のディメンションの共通部分にファクトが格納される領域)を使用します。図21-1に、あるデータ・キューブと、このデータ・キューブが様々なグループによって異なる方法で利用される様子を示します。キューブには、製品、市場、売上および時間のディメンションで編成された売上データが格納されています。これが単方向ディメンションであることに注意してください。実際のデータは通常の表に物理的に格納されます。キューブ・データは、詳細データと集計データの両方で構成されます。

図21-1 論理キューブおよび異なるユーザーごとのビュー

キューブからデータのスライスを取り出すことができます。これらは、表21-1に示すようなクロス集計レポートに対応します。地域マネージャは、異なる市場に適用されるキューブ・スライスを比較することで、データを解析します。これとは対照的に、製品マネージャは、異なる製品に適用するスライスを比較します。非定型作業を行うユーザーは、サブセット・キューブ内で、様々にデータを絞り込んで処理できます。

多次元の質問への回答には、多くの場合、数百万行にもなる膨大な量のデータへのアクセスおよび問合せが伴います。巨大な組織によって生成される大量のディテール・データは、最低レベルでは解析できないため、情報の集計ビューが不可欠です。合計やカウントなど、多数のディメンションにまたがる集計は、多次元分析にとってきわめて重要です。したがって、分析作業には、便利で効率的なデータ集計が必要となります。

最適化されたパフォーマンス

多次元での処理のみでなく、すべてのタイプの処理が、拡張された集計機能の効果を得ることができます。トランザクション処理システムや、財務システム、製造システムでは、大量のシステム・リソースを必要とする膨大な数の成果レポートが生成されます。これらのレポート作成時の効率が向上することで、システムの負荷が削減されます。実際、データを詳細レベルから高度なレベルまで集計する場合には、どのようなコンピュータ処理でも集計パフォーマンスの最適化によるメリットが得られます。

これらの拡張された集計機能の提供によって、次に示すような多くの効果が得られます。

- 大量の作業にも少量のSQLコードしか必要としない単純化されたプログラム

- より高速で高効率の問合せ処理

- 集計作業がサーバー側に移行されることによる、クライアント処理の負荷およびネットワーク通信量の削減

- 類似した問合せで既存の作業を効率化できることによる、集計のキャッシング機会の増加

集計の使用例

GROUP BY拡張の使用例を示すために、この章ではサンプル・スキーマのshのデータを使用します。この章の例はすべて、この会社のデータを例として使用します。この架空の会社は世界中で販売を行っており、売上を金額情報と数量情報の両面から追跡しています。多数のデータ行があるため、問合せは、この例のように通常はWHERE句で厳密に制限され、結果は少数の行に限定されます。

例21-1 単純なクロス集計レポート(小計付き)

表21-1は、クロス集計レポートの例です。このレポートは、2000年9月のインターネット販売および直接販売における、米国(US)とフランス(France)のcountry_idおよびchannel_desc別の総売上を示しています。

表21-1 単純なクロス集計レポート(小計付き)

チャネル

国

France

US

合計

インターネット

9,597

124,224

133,821

直接販売

61,202

638,201

699,403

合計

70,799

762,425

833,224

値の数が9個のみのこのような単純なレポートでも、4つの小計および1つの総計が生成されています。このレポートに必要な値の半数は、SUM(amount_sold)を要求してGROUP BY(channel_desc, country_id)を行うような問合せのみでは計算されません。上位レベルの集計を取得するには、さらに問合せが必要になります。小計の計算について改善されたデータベース・コマンドによって、問合せ、レポートおよび分析的な操作で大きな効果を得ることができます。

SELECT channels.channel_desc, countries.country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id AND
sales.channel_id= channels.channel_id AND channels.channel_desc IN
('Direct Sales', 'Internet') AND times.calendar_month_desc='2000-09'
AND customers.country_id=countries.country_id
AND countries.country_iso_code IN ('US','FR')
GROUP BY CUBE(channels.channel_desc, countries.country_iso_code);

CHANNEL_DESC         CO SALES$
-------------------- -- --------------
833,224
FR         70,799
US        762,425
Internet                       133,821
Internet             FR          9,597
Internet             US        124,224
Direct Sales                   699,403
Direct Sales         FR         61,202
Direct Sales         US        638,201

例内のNULLの解釈

GROUP BYの拡張機能によって戻されるNULLは、不明値を意味する従来のNULLとは限りません。このNULLは、その行が小計であることを示す場合があります。データベース・システムに値以外のものを導入するのを避けるため、こうした小計値には特別なタグは付けられていません。

関連項目:
小計を表すNULLとデータに格納されるNULLを区別する方法の詳細は、「GROUPING関数」を参照してください。

ROLLUP(GROUP BYの拡張)

ROLLUPを使用すると、SELECT文により、指定したディメンション・グループの小計を複数のレベルで計算できます。総計も計算できます。ROLLUPは、GROUP BY句の単純な拡張であるため、その構文は非常に簡単です。ROLLUPによる拡張は非常に効率的で、問合せにかかるオーバーヘッドは最小限に抑えられます。

ROLLUPのアクションは簡単です。これは、最も詳細なレベルから総計まで、ROLLUP句で指定されたグループ・リストに従ってロールアップする小計を作成します。ROLLUPは、その引数として、グルーピング列の順序付けリストを取ります。最初に、GROUP BY句で指定された標準の集計値を計算します。次に、グルーピング列のリストを右から左に移動しながら、順番に高いレベルの小計を作成します。最後に、総計を作成します。

ROLLUPは、n+1のレベルで小計を作成します。ここで、nはグルーピング列の数です。たとえば、time、regionおよびdepartment(n=3)のグルーピング列でROLLUPを指定した問合せの場合、結果セットには4つの集計レベルの行が含まれます。

ROLLUPを使用するときにデータの圧縮が必要となることがあります。これは、古いパーティションに対する更新が少ない場合に特に役立ちます。

ROLLUPを使用するとき

小計を伴う作業では、ROLLUPによる拡張を使用します。

- 時間や地理などの階層的なディメンションに従って小計する場合に非常に有効です。たとえば、問合せでROLLUP(y, m, day)またはROLLUP(country, state, city)のように指定できます。

- サマリー表を使用しているデータ・ウェアハウス管理者の場合は、ROLLUPを使用することでサマリー表のメンテナンスが簡単になり、時間を短縮できます。

ROLLUPの構文

ROLLUPは、SELECT文のGROUP BY句で使用します。形式は次のとおりです。

SELECT … GROUP BY ROLLUP(grouping_column_reference_list)

例21-2 ROLLUP

この例では、shサンプル・スキーマ・データを使用します。このデータは、図21-1で使用されているものと同じです。ROLLUPは、3つのディメンションにまたがっています。

SELECT channels.channel_desc, calendar_month_desc,
countries.country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id
AND sales.cust_id=customers.cust_id
AND customers.country_id = countries.country_id
AND sales.channel_id = channels.channel_id
AND channels.channel_desc IN ('Direct Sales', 'Internet')
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND countries.country_iso_code IN ('GB', 'US')
GROUP BY
ROLLUP(channels.channel_desc, calendar_month_desc, countries.country_iso_code);

CHANNEL_DESC         CALENDAR CO SALES$
-------------------- -------- -- --------------
Internet             2000-09  GB         16,569
Internet             2000-09  US        124,224
Internet             2000-09            140,793
Internet             2000-10  GB         14,539
Internet             2000-10  US        137,054
Internet             2000-10            151,593
Internet                                292,387
Direct Sales         2000-09  GB         85,223
Direct Sales         2000-09  US        638,201
Direct Sales         2000-09            723,424
Direct Sales         2000-10  GB         91,925
Direct Sales         2000-10  US        682,297
Direct Sales         2000-10            774,222
Direct Sales                          1,497,646
1,790,032

丸めのため、結果が常に加算されるとは限らないことに注意してください。

この問合せでは、次の行集合が戻されます。

- ROLLUPを使用しないでGROUP BYによって生成される通常の集計行。

- channel_descとcalendar_monthの組合せごとに、country_idをまたがって集計される第1レベルの小計。

- channel_desc値ごとに、calendar_month_descとcountry_idをまたがって集計される第2レベルの小計。

- 総計行。

部分的ROLLUP

一部の小計のみを含めるためのロールアップもできます。このような部分的ロールアップで使用する構文は次のとおりです。

GROUP BY expr1, ROLLUP(expr2, expr3);

この場合、GROUP BY句は3つ(2+1)の集計レベルで小計を作成します。つまり、(expr1, expr2, expr3)のレベル、(expr1, expr2)のレベル、および(expr1)のレベルです。

例21-3 部分的ROLLUP

SELECT channel_desc, calendar_month_desc, countries.country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id
AND customers.country_id = countries.country_id
AND sales.channel_id= channels.channel_id
AND channels.channel_desc IN ('Direct Sales', 'Internet')
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND countries.country_iso_code IN ('GB', 'US')
GROUP BY channel_desc, ROLLUP(calendar_month_desc, countries.country_iso_code);

CHANNEL_DESC         CALENDAR CO SALES$
-------------------- -------- -- --------------
Internet             2000-09  GB         16,569
Internet             2000-09  US        124,224
Internet             2000-09            140,793
Internet             2000-10  GB         14,539
Internet             2000-10  US        137,054
Internet             2000-10            151,593
Internet                                292,387
Direct Sales         2000-09  GB         85,223
Direct Sales         2000-09  US        638,201
Direct Sales         2000-09            723,424
Direct Sales         2000-10  GB         91,925
Direct Sales         2000-10  US        682,297
Direct Sales         2000-10            774,222
Direct Sales                          1,497,646

この問合せでは、次の行集合が戻されます。

- ROLLUPを使用しないでGROUP BYによって生成される通常の集計行。

- channel_descとcalendar_month_descの組合せごとに、country_idをまたがって集計される第1レベルの小計。

- channel_desc値ごとに、calendar_month_descとcountry_idをまたがって集計される第2レベルの小計。

- 総計行は生成されません。

CUBE(GROUP BYの拡張)

CUBEは、指定されたグルーピング列の集合を取り、それらが取り得るすべての組合せに対して小計を作成します。多次元分析の観点では、CUBEは、指定されたディメンションを持つデータ・キューブに対して計算されるすべての小計を生成します。CUBE(time, region, department)を指定した場合、結果セットには、同等のROLLUP文および追加組合せ内にある値がすべて含まれます。たとえば、図21-1では、すべての地域にわたる部門の合計(279,000および319,000)はROLLUP(time, region, department)句では計算されませんが、CUBE(time, region, department)句では計算されます。CUBEに対して指定された列がn個ある場合、戻される小計の組合せは2からn個になります。例21-4に、3つのディメンションを持つキューブの例を示します。

関連項目:
構文および制限の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

CUBEを使用するとき

クロス集計レポートを必要とする状況では、CUBEの使用を考慮してください。クロス集計レポートに必要なデータは、CUBEを使用して単一のSELECTで作成できます。ROLLUPと同様に、CUBEもサマリー表の作成に有効です。CUBE問合せがパラレルに実行されるよりサマリー表を利用した方が高速です。

CUBEは、通常、1つのディメンションの異なるレベルを表す列を使用する問合せより、複数のディメンションの列を使用する問合せに最も適しています。たとえば、一般的に要求されるクロス集計作成では、month、stateおよびproductのすべての組合せに対する小計が必要です。これらは、3つの独立したディメンションであり、取り得るすべての組合せに対する小計を処理した分析が一般的です。反対に、year、monthおよびdayが取り得るすべての組合せを示すクロス集計作成では、timeディメンションに階層があるため、必要な値はいくつかに限られています。年間を通して合計された、毎月の日別利益のような小計は、ほとんどの分析では必要ありません。「年間の毎月16日の総売上はいくらか」といった質問を必要とするユーザーは、比較的少数です。ロールアップ計算を効率的に処理する例は、「ROLLUPおよびCUBEでの階層処理」を参照してください。

CUBEの構文

CUBEは、SELECT文のGROUP BY句で使用します。形式は次のとおりです。

SELECT …  GROUP BY CUBE (grouping_column_reference_list)

例21-4 CUBE

SELECT channel_desc, calendar_month_desc, countries.country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id AND
sales.channel_id= channels.channel_id
AND customers.country_id = countries.country_id
AND channels.channel_desc IN
('Direct Sales', 'Internet') AND times.calendar_month_desc IN
('2000-09', '2000-10') AND countries.country_iso_code IN ('GB', 'US')
GROUP BY CUBE(channel_desc, calendar_month_desc, countries.country_iso_code);

CHANNEL_DESC         CALENDAR CO SALES$
-------------------- -------- -- --------------
1,790,032
GB        208,257
US      1,581,775
2000-09            864,217
2000-09  GB        101,792
2000-09  US        762,425
2000-10            925,815
2000-10  GB        106,465
2000-10  US        819,351
Internet                                292,387
Internet                      GB         31,109
Internet                      US        261,278
Internet             2000-09            140,793
Internet             2000-09  GB         16,569
Internet             2000-09  US        124,224
Internet             2000-10            151,593
Internet             2000-10  GB         14,539
Internet             2000-10  US        137,054
Direct Sales                          1,497,646
Direct Sales                  GB        177,148
Direct Sales                  US      1,320,497
Direct Sales         2000-09            723,424
Direct Sales         2000-09  GB         85,223
Direct Sales         2000-09  US        638,201
Direct Sales         2000-10            774,222
Direct Sales         2000-10  GB         91,925
Direct Sales         2000-10  US        682,297

この問合せは、3つのディメンションにまたがるCUBE集計を示しています。

部分的CUBE

部分的CUBEは、特定のディメンションに制限してCUBE演算子の外側の列に進むという点で、部分的ROLLUPに似ています。この場合、取り得るすべての組合せに対する小計は、CUBEリスト内(カッコ内)のディメンションに制限され、GROUP BYリスト内の前の項目と組み合されます。

部分的CUBEの構文は、次のとおりです。

GROUP BY expr1, CUBE(expr2, expr3)

この構文例では2×2、つまり次の4つの小計が計算されます。詳細は次のとおりです。

- (expr1, expr2, expr3)

- (expr1, expr2)

- (expr1, expr3)

- (expr1)

例21-5 部分的CUBE

salesデータベースを使用して、次の文を発行できます。

SELECT channel_desc, calendar_month_desc, countries.country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id = times.time_id
AND sales.cust_id = customers.cust_id
AND customers.country_id=countries.country_id
AND sales.channel_id = channels.channel_id
AND channels.channel_desc IN ('Direct Sales', 'Internet')
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND countries.country_iso_code IN ('GB', 'US')
GROUP BY channel_desc, CUBE(calendar_month_desc, countries.country_iso_code);

CHANNEL_DESC         CALENDAR CO SALES$
-------------------- -------- -- --------------
Internet                                292,387
Internet                      GB         31,109
Internet                      US        261,278
Internet             2000-09            140,793
Internet             2000-09  GB         16,569
Internet             2000-09  US        124,224
Internet             2000-10            151,593
Internet             2000-10  GB         14,539
Internet             2000-10  US        137,054
Direct Sales                          1,497,646
Direct Sales                  GB        177,148
Direct Sales                  US      1,320,497
Direct Sales         2000-09            723,424
Direct Sales         2000-09  GB         85,223
Direct Sales         2000-09  US        638,201
Direct Sales         2000-10            774,222
Direct Sales         2000-10  GB         91,925
Direct Sales         2000-10  US        682,297

CUBEを使用しない小計の計算

ROLLUPの場合と同様に、UNION ALL文と組み合された複数のSELECT文によって、CUBEを使用する場合と同じ情報が収集できます。ただし、この場合は多数のSELECT文が必要となります。nディメンションのキューブの場合、2からn個のSELECT文が必要です。3ディメンションの場合も、UNION ALLでリンクされたSELECT文を発行することになります。SELECT文が多すぎるため、処理の効率が悪くなり、SQL文が極端に長くなります。

可能なすべての組合せを計算する際にディメンションを1つのみ追加すると、どのような影響があるかを考えてみます。SELECT文の数は、2倍の16になります。CUBE句で使用される列が増加するほど、UNION ALLを使用する方法と比較した場合の効果も大きくなります。

GROUPING関数

ROLLUPおよびCUBEの使用については、2つの課題があります。第1の課題は、どの結果セット行が小計であるかをプログラム上でどのように判断するか、および指定された小計の正確な集計レベルをどのように探し出すかということです。合計に対する割合などを計算する場合に小計がよく必要となるため、どの行が求める小計であるかを判断する簡単な方法が必要です。第2の課題は、格納されるNULL値と、ROLLUPまたはCUBEによって作成される「NULL」値の両方が参照結果に含まれる場合、どう処理するかということです。この2つをどのように区別するかが問題になります。ここでは、このような場合について例をあげて説明します。

関連項目:
構文および制限の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

GROUPING関数

GROUPINGは、このような問題を処理します。単一の列を引数として使用し、ROLLUPまたはCUBE操作によってNULLが作成された場合に、GROUPINGは1を戻します。つまり、NULLが小計の行であることを示す場合、GROUPINGは1を戻します。格納されたNULLなど、その他のタイプの値では0(ゼロ)を戻します。

GROUPINGは、SELECT文のリスト部分で使用します。形式は次のとおりです。

SELECT …  [GROUPING(dimension_column)…]  …
GROUP BY …    {CUBE | ROLLUP| GROUPING SETS}  (dimension_column)

例21-6 GROUPINGによる列のマスク

次の例では、GROUPINGを使用して、例21-3に示した結果セットに対する一連のマスク列を作成します。マスク列は、プログラムで簡単に分析できます。

SELECT channel_desc, calendar_month_desc, country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$, GROUPING(channel_desc) AS Ch,
GROUPING(calendar_month_desc) AS Mo, GROUPING(country_iso_code) AS Co
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id
AND sales.cust_id=customers.cust_id
AND customers.country_id = countries.country_id
AND sales.channel_id= channels.channel_id
AND channels.channel_desc IN ('Direct Sales', 'Internet')
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND countries.country_iso_code IN ('GB', 'US')
GROUP BY ROLLUP(channel_desc, calendar_month_desc, countries.country_iso_code);

CHANNEL_DESC         CALENDAR CO SALES$                 CH         MO         CO
-------------------- -------- -- -------------- ---------- ---------- ----------
Internet             2000-09  GB         16,569          0          0          0
Internet             2000-09  US        124,224          0          0          0
Internet             2000-09            140,793          0          0          1
Internet             2000-10  GB         14,539          0          0          0
Internet             2000-10  US        137,054          0          0          0
Internet             2000-10            151,593          0          0          1
Internet                                292,387          0          1          1
Direct Sales         2000-09  GB         85,223          0          0          0
Direct Sales         2000-09  US        638,201          0          0          0
Direct Sales         2000-09            723,424          0          0          1
Direct Sales         2000-10  GB         91,925          0          0          0
Direct Sales         2000-10  US        682,297          0          0          0
Direct Sales         2000-10            774,222          0          0          1
Direct Sales                          1,497,646          0          1          1
1,790,032          1          1          1

プログラムは、T、RおよびD列に対するマスク「0 0 0」によって、ディテール行を簡単に識別できます。第1レベルの小計行は「0 0 1」のマスクを持ち、第2レベルの小計行はマスク「0 1 1」、全体の総計行はマスク「1 1 1」を持ちます。

例21-7に示すようにGROUPINGおよびDECODE関数を使用して、結果セットを読みやすくできます。

例21-7 可読性を高めるためのGROUPING

SELECT DECODE(GROUPING(channel_desc), 1, 'Multi-channel sum', channel_desc) AS
Channel, DECODE (GROUPING (country_iso_code), 1, 'Multi-country sum',
country_iso_code) AS Country, TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id
AND sales.cust_id=customers.cust_id
AND customers.country_id = countries.country_id
AND sales.channel_id= channels.channel_id
AND channels.channel_desc IN ('Direct Sales', 'Internet')
AND times.calendar_month_desc= '2000-09'
AND country_iso_code IN ('GB', 'US')
GROUP BY CUBE(channel_desc, country_iso_code);

CHANNEL              COUNTRY           SALES$
-------------------- ----------------- --------------
Multi-channel sum    Multi-country sum        864,217
Multi-channel sum    GB                       101,792
Multi-channel sum    US                       762,425
Internet             Multi-country sum        140,793
Internet             GB                        16,569
Internet             US                       124,224
Direct Sales         Multi-country sum        723,424
Direct Sales         GB                        85,223
Direct Sales         US                       638,201

前述の文を理解するために、channel_desc列を処理する最初の列指定に注目してください。前述の文の最初の行を考えてみます。

SELECT DECODE(GROUPING(channel_desc), 1, 'All Channels', channel_desc)AS Channel

ここで、channel_descの値は、GROUPING関数を含むDECODE関数で決定されます。行の値がROLLUPまたはCUBEによって作成された集計である場合、GROUPING関数は1を戻し、それ以外の場合は0(ゼロ)を戻します。次に、DECODE関数は、GROUPING関数の結果を処理します。1が戻された場合は、テキスト「All Channels」が戻されます。0(ゼロ)が戻された場合、データベースからchannel_descの値が戻されます。データベースから戻される値は、「Internet」のような実際の値または格納されたNULLです。country_idを表示する2番目の列指定も同様に処理されます。

GROUPINGを使用するとき

GROUPING関数は、2種類のNULLの識別に役立つのみでなく、小計行のソートまたは結果のフィルタも可能です。例21-8では、CUBEによって作成された小計のサブセットを取り出し、基本レベルの集計は取り出しません。HAVING句で、GROUPING関数を使用する列を制約します。

例21-8 HAVINGと組み合せたGROUPING

SELECT channel_desc, calendar_month_desc, country_iso_code, TO_CHAR(
SUM(amount_sold), '9,999,999,999') SALES$, GROUPING(channel_desc) CH, GROUPING
(calendar_month_desc)  MO, GROUPING(country_iso_code) CO
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id
AND customers.country_id = countries.country_id
AND sales.channel_id= channels.channel_id
AND channels.channel_desc IN ('Direct Sales', 'Internet')
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND country_iso_code IN ('GB', 'US')
GROUP BY CUBE(channel_desc, calendar_month_desc, country_iso_code)
HAVING (GROUPING(channel_desc)=1 AND GROUPING(calendar_month_desc)= 1
AND GROUPING(country_iso_code)=1) OR (GROUPING(channel_desc)=1
AND GROUPING (calendar_month_desc)= 1) OR (GROUPING(country_iso_code)=1
AND GROUPING(calendar_month_desc)= 1);

CHANNEL_DESC         C CO SALES$                 CH         MO         CO
-------------------- - -- -------------- ---------- ---------- ----------
US      1,581,775          1          1          0
GB        208,257          1          1          0
Direct Sales                   1,497,646          0          1          1
Internet                         292,387          0          1          1
1,790,032          1          1          1

例21-8でグループが正確に指定されていることを確認するために、例21-8の結果セットと例21-3の結果セットを比較します。前者には、timeおよびdepartmentについて集計された年間合計、地域合計および総計のみが含まれています。

GROUPING_ID関数

特定の行のGROUP BYレベルを調べるには、問合せでGROUP BY列ごとにGROUPING関数情報を戻す必要があります。そのためにGROUPING関数を使用する場合は、各GROUP BY列にGROUPING関数を使用するもう1つの列が必要です。たとえば、4列のGROUP BY句は4つのGROUPING関数を使用して分析する必要があります。これは、SQLで記述するには不便であり、問合せに必要な列の数が増加します。問合せの結果セットを表に格納する場合は、マテリアライズド・ビューの場合と同様に余分な列により記憶領域が使用されます。

このような問題に対処するために、GROUPING_ID関数を使用できます。GROUPING_IDは、正確なGROUP BYレベルを判断できるように、単一の数値を戻します。GROUPING_IDは、行ごとに、該当するGROUPING関数を使用した場合に生成される1と0のセットを取り、それを連結してビット・ベクトルを形成します。このビット・ベクトルは2進数として扱われ、GROUPING_ID関数はこの数値の10進値を戻します。たとえば、式CUBE(a, b)でグルーピングする場合、可能な値は表21-2のようになります。

表21-2 CUBE(a, b)のGROUPING_IDの例

集計レベル

ビット・ベクトル

GROUPING_ID

a, b

0 0

0

a

0 1

1

b

1 0

2

総計

1 1

3

GROUPING_IDでは、グルーピング・セット指定により作成されたグルーピングが明確に区別されるため、マテリアライズド・ビューのリフレッシュおよびリライトに非常に有効です。

GROUP_ID関数

GROUP BYの拡張は強力で柔軟性があり、重複するグルーピングを含む複雑な結果セットの出力も可能です。GROUP_ID関数を使用すると、重複するグルーピングを区別できます。特定レベルで計算される複数の行集合がある場合、GROUP_IDは最初の集合の行すべてに値0を割り当てます。特定のグルーピングに関する重複行の他の集合にはすべて、1から始まる上位の値が割り当てられます。たとえば、重複するグルーピングを生成する次の問合せを考えてみます。

例21-9 GROUP_ID

SELECT country_iso_code, SUBSTR(cust_state_province,1,12), SUM(amount_sold),
GROUPING_ID(country_iso_code, cust_state_province) GROUPING_ID, GROUP_ID()
FROM sales, customers, times, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id
AND customers.country_id=countries.country_id AND times.time_id= '30-OCT-00'
AND country_iso_code IN ('FR', 'ES')
GROUP BY GROUPING SETS (country_iso_code,
ROLLUP(country_iso_code, cust_state_province));

CO SUBSTR(CUST_ SUM(AMOUNT_SOLD) GROUPING_ID GROUP_ID()
-- ------------ ---------------- ----------- ----------
ES Alicante               135.32           0          0
ES Valencia              4133.56           0          0
ES Barcelona               24.22           0          0
FR Centre                   74.3           0          0
FR Aquitaine              231.97           0          0
FR Rhtne-Alpes           1624.69           0          0
FR Ile-de-Franc          1860.59           0          0
FR Languedoc-Ro           4287.4           0          0
12372.05           3          0
ES                        4293.1           1          0
FR                       8078.95           1          0
ES                        4293.1           1          1
FR                       8078.95           1          1

この問合せでは、(country_id, cust_state_province)、(country_id)、(country_id)および()というグルーピングが生成されます。グルーピング(country_id)が2度繰り返されていることに注意してください。GROUPING SETSの構文については、「GROUPING SETS式」を参照してください。

この関数を使用すると、結果にフィルタを適用して重複するグルーピングを排除できます。たとえば、問合せにHAVING句の条件GROUP_ID()=0を追加し、前述の例から重複するグルーピング(region)を排除できます。

GROUPING SETS式

GROUP BY句の中でGROUPING SETS式を使用して、作成するグループの集合を選択的に指定できます。これにより、CUBE全体を計算せずに、複数のディメンションにまたがる正確な指定ができます。次に例を示します。

SELECT channel_desc, calendar_month_desc, country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id AND
sales.channel_id= channels.channel_id AND channels.channel_desc IN
('Direct Sales', 'Internet') AND times.calendar_month_desc IN
('2000-09', '2000-10') AND country_iso_code IN ('GB', 'US')
GROUP BY GROUPING SETS((channel_desc, calendar_month_desc, country_iso_code),
(channel_desc, country_iso_code), (calendar_month_desc, country_iso_code));

この文は複合列を使用していることに注意してください。詳細は、「複合列」を参照してください。この文では、次の3つのグルーピングにまたがる集計が計算されます。

- (channel_desc, calendar_month_desc, country_iso_code)

- (channel_desc, country_iso_code)

- (calendar_month_desc, country_iso_code)

前述の文を次の代替文と比較します。次の文は、CUBE操作とGROUPING_ID関数を使用して必要な行を戻します。

SELECT channel_desc, calendar_month_desc, country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$,
GROUPING_ID(channel_desc, calendar_month_desc, country_iso_code) gid
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id AND
sales.channel_id= channels.channel_id AND channels.channel_desc IN
('Direct Sales', 'Internet') AND times.calendar_month_desc IN
('2000-09', '2000-10') AND country_iso_code IN ('GB', 'US')
GROUP BY CUBE(channel_desc, calendar_month_desc, country_iso_code)
HAVING GROUPING_ID(channel_desc, calendar_month_desc, country_iso_code)=0
OR GROUPING_ID(channel_desc, calendar_month_desc, country_iso_code)=2
OR GROUPING_ID(channel_desc, calendar_month_desc, country_iso_code)=4;

この文では、8(2×2×2)のグルーピングがすべて計算されますが、必要としているのは上の3グループのみです。

もう1つの方法は次の文ですが、複数の組合せがあるため長くなります。この文では、実表を3回スキャンする必要があり、非効率的です。CUBEとROLLUPは、きわめて限定的な意味を持つグルーピング・セットとみなすことができます。たとえば、次の文を考えてみます。

CUBE(a, b, c)

この文は、次の文と同等です。

GROUPING SETS ((a, b, c), (a, b), (a, c), (b, c), (a), (b), (c), ())
ROLLUP(a, b, c)

さらに、この文は、次の文と同等です。

GROUPING SETS ((a, b, c), (a, b), ())

GROUPING SETSの構文

GROUPING SETSの構文では、同じ問合せで複数のグルーピングを定義できます。GROUP BYでは、指定したグルーピングがすべて計算され、UNION ALLと組み合されます。たとえば、次の文を考えてみます。

GROUP BY GROUPING sets (channel_desc, calendar_month_desc, country_id )

この文は、次の文と同等です。

GROUP BY channel_desc UNION ALL
GROUP BY calendar_month_desc UNION ALL GROUP BY country_id

表21-3に、グルーピング・セット指定および同等のGROUP BY指定を示します。一部の例では複合列が使用されているため注意してください。

表21-3 GROUPING SETS文および同等のGROUP BY文

GROUPING SETS文

同等のGROUP BY文

GROUP BY GROUPING SETS(a, b, c)

GROUP BY a UNION ALL GROUP BY b UNION ALL GROUP BY c

GROUP BY GROUPING SETS(a, b, (b, c))

GROUP BY a UNION ALL GROUP BY b UNION ALL GROUP BY b, c

GROUP BY GROUPING SETS((a, b, c))

GROUP BY a, b, c

GROUP BY GROUPING SETS(a, (b), ())

GROUP BY a UNION ALL GROUP BY b UNION ALL GROUP BY ()

GROUP BY GROUPING SETS(a, ROLLUP(b, c))

GROUP BY a UNION ALL GROUP BY ROLLUP(b, c)

問合せブロックにまたがって検索して実行計画を生成するオプティマイザがなければ、UNIONに基づく問合せでは実表salesを複数回スキャンする必要があります。通常、ファクト表は大型のため、これはきわめて非効率的です。GROUPING SETS文を使用すると、必要なすべてのグルーピングを同じ問合せブロック内で使用できます。

複合列

複合列は、グルーピングの計算中に1単位として処理される列のコレクションです。次の文のように、列をカッコで囲んで指定します。

ROLLUP (year, (quarter, month), day)

この文では、データは年から四半期にまたがってはロールアップされませんが、かわりに、UNION ALLの次のグルーピングと同等化されます。

- (year, quarter, month, day),

- (year, quarter, month),

- (year)

- ()

(quarter, month)は複合列を形成し、1単位として処理されます。通常、複合列は、ROLLUP、CUBE、GROUPING SETSおよび連結されたグルーピングに有効です。たとえば、CUBEまたはROLLUPでは、複合列は特定レベルにまたがる集計がスキップされることを意味します。つまり、次の文になります。

GROUP BY ROLLUP(a, (b, c))

これは、次と同等です。

GROUP BY a, b, c UNION ALL
GROUP BY a UNION ALL
GROUP BY ()

(b, c)は1単位として処理され、(b, c)にまたがるロールアップは適用されません。たとえば、zの場合、(b, c)およびGROUP BY式はGROUP BY ROLLUP(a, z)に減少します。これを次の通常のロールアップと比較します。

GROUP BY ROLLUP(a, b, c)

これは、次のようになります。

GROUP BY a, b, c UNION ALL
GROUP BY a, b UNION ALL
GROUP BY a UNION ALL
GROUP BY ().

同様に、次の文は4つのGROUP BYと同等です。

GROUP BY CUBE((a, b), c)

GROUP BY a, b, c UNION ALL
GROUP BY a, b UNION ALL
GROUP BY c UNION ALL
GROUP By ()

GROUPING SETSでは、複合列はGROUP BYの特定レベルを示すために使用されます。複合列の他の例については、表21-3を参照してください。

例21-10 複合列

CUBEとROLLUPで必要な集計レベルは、完全には制御できません。たとえば、次の文を考えてみます。

SELECT channel_desc, calendar_month_desc, country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id
AND customers.country_id = countries.country_id
AND sales.channel_id= channels.channel_id
AND channels.channel_desc IN ('Direct Sales', 'Internet')
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND country_iso_code IN ('GB', 'US')
GROUP BY ROLLUP(channel_desc, calendar_month_desc, country_iso_code);

この文では、Oracleにより次のグルーピングが計算されます。

- (channel_desc, calendar_month_desc, country_iso_code)

- (channel_desc, calendar_month_desc)

- (channel_desc)

- ()

これらのうち、1、3および4番目のグルーピングのみが必要な場合に、計算をこれらのグルーピングに制限するには、複合列を使用する必要があります。複合列を使用すると、月と国をロールアップ中に1単位として処理することで計算を制限できます。カッコ内の列は、CUBEおよびROLLUPの計算中に1単位として処理されます。つまり、次のようになります。

SELECT channel_desc, calendar_month_desc, country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id AND
sales.channel_id= channels.channel_id  AND channels.channel_desc IN
('Direct Sales', 'Internet') AND times.calendar_month_desc IN
('2000-09', '2000-10') AND country_iso_code IN ('GB', 'US')
GROUP BY ROLLUP(channel_desc, (calendar_month_desc, country_iso_code));

CHANNEL_DESC         CALENDAR CO SALES$
-------------------- -------- -- --------------
Internet             2000-09  GB        228,241
Internet             2000-09  US        228,241
Internet             2000-10  GB        239,236
Internet             2000-10  US        239,236
Internet                                934,955
Direct Sales         2000-09  GB      1,217,808
Direct Sales         2000-09  US      1,217,808
Direct Sales         2000-10  GB      1,225,584
Direct Sales         2000-10  US      1,225,584
Direct Sales                          4,886,784
5,821,739

連結グルーピング

連結グルーピングにより、一貫した方法でグルーピングの有効な組合せを生成できます。連結グルーピングで指定したグルーピングにより、各グルーピング・セットからのグルーピングのクロス積が得られます。クロス積操作により、ごく少数の連結グルーピングで多数の最終グループを生成できます。複数のグルーピング・セット、キューブおよびロールアップをカンマで区切って指定するのみで、連結グルーピングを指定できます。連結グルーピング・セットの例を次に示します。

GROUP BY GROUPING SETS(a, b), GROUPING SETS(c, d)

このSQLは、次のグルーピングを定義します。

(a, c), (a, d), (b, c), (b, d)

グルーピング・セットの連結は、次の理由できわめて有効です。

- 問合せの開発が簡単
すべてのグルーピングを手動で列挙する必要がありません。

- アプリケーションで使用
分析用アプリケーションで生成されるSQLでは、グルーピング・セットの連結を伴うことがよくあります。この場合、それぞれのグルーピング・セットがディメンションに必要なグルーピングを定義します。

例21-11 連結グルーピング

GROUP BY句に複数のグルーピングを指定することもできます。たとえば、各製品の売上値をtimeディメンション(year、monthおよびday)のすべてのレベルとgeographyディメンション(region)のすべてのレベルにまたがってロールアップして集計する場合は、次の文を発行できます。

SELECT channel_desc, calendar_year, calendar_quarter_desc, country_iso_code,
cust_state_province, TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id = times.time_id AND sales.cust_id = customers.cust_id
AND sales.channel_id = channels.channel_id AND countries.country_id =
customers.country_id AND channels.channel_desc IN
('Direct Sales', 'Internet') AND times.calendar_month_desc IN ('2000-09',
'2000-10') AND countries.country_iso_code IN ('GB', 'FR')
GROUP BY channel_desc, GROUPING SETS (ROLLUP(calendar_year,
calendar_quarter_desc),
ROLLUP(country_iso_code, cust_state_province));

その結果、次のようなグルーピングとなります。

- (channel_desc, calendar_year, calendar_quarter_desc)

- (channel_desc, calendar_year)

- (channel_desc)

- (channel_desc, country_iso_code, cust_state_province)

- (channel_desc, country_iso_code)

- (channel_desc)

これは、次のクロス積です。

- channel_desc

- ROLLUP(calendar_year, calendar_quarter_desc)。これは、((calendar_year, calendar_quarter_desc), (calendar_year), ())と同等です。

- ROLLUP(country_iso_code, cust_state_province)。これは、((country_iso_code, cust_state_province), (country_iso_code), ())と同等です。

出力には(channel_desc)グループが2つ含まれていることに注意してください。余分な(channel_desc)グループを排除するには、問合せでGROUP_ID関数を使用します。

連結結合のもう1つの例を例21-12に示します。この例は、2つのグルーピング・セットのクロス積を示しています。

例21-12 連結グルーピング(2つのグルーピング・セットのクロス積)

SELECT country_iso_code, cust_state_province, calendar_year,
calendar_quarter_desc, TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id AND
countries.country_id=customers.country_id  AND
sales.channel_id= channels.channel_id AND channels.channel_desc IN
('Direct Sales', 'Internet') AND times.calendar_month_desc IN
('2000-09', '2000-10') AND country_iso_code IN ('GB', 'FR')
GROUP BY GROUPING SETS (country_iso_code, cust_state_province),
GROUPING SETS (calendar_year, calendar_quarter_desc);

この文では、次のようなグルーピングが計算されます。

- (country_iso_code, year)、(country_iso_code, calendar_quarter_desc)、(cust_state_province, year)および(cust_state_province, calendar_quarter_desc)

連結グルーピングと階層的データ・キューブ

連結グルーピングの最も重要な用途の1つは、階層的データ・キューブに必要な集計を生成することです。階層的キューブは、データが各ディメンションのロールアップ階層に沿って集計され、これらの集計がディメンション間で組み合されるデータセットです。ビジネス・インテリジェンス問合せに必要な典型的な集計の集合が含まれます。連結グルーピングを使用すると、n個のROLLUPを使用するのみで階層キューブに必要な集計をすべて生成し、不要な集計の生成を回避できます。nはディメンション数です。

shサンプル・スキーマ・データセットのディメンションが3つのみで、それぞれにマルチレベルの階層があるとします。

- time: year、quarter、month、day(weekは別の階層内)

- product: category、subcategory、prod_name

- geography: region、subregion、country、state、city

このデータは、階層レベルごとに1列を使用して表され、ディメンションの列は合計12列および売上高を保持する列となります。

ビジネス・インテリジェンスのニーズに合せ、ディメンションの様々な組合せについて特定の集計を計算して格納できます。例21-13では、「day」を除くすべてのレベルの集計を作成しますが、これでは作成する行数が多すぎます。特に、各ディメンション内でROLLUPを使用して有効な集計を生成する必要があります。各ディメンションでROLLUPベースの集計を生成した後に、それを他のディメンションと組み合せます。これにより、階層的キューブが生成されます。これは12のディメンション列すべてを使用するCUBEとまったく同じではないため注意してください。2から12乗(4,096)の集計グループが作成されますが、そのうちで必要としているのはごく少数です。連結グルーピング・セットを使用すると、必要な集計のみを簡単に生成できます。例21-13に、GROUP BY句が必要とされる例を示します。

例21-13 連結グルーピングと階層的キューブ

SELECT calendar_year, calendar_quarter_desc, calendar_month_desc,
country_region, country_subregion, countries.country_iso_code,
cust_state_province, cust_city, prod_category_desc, prod_subcategory_desc,
prod_name, TO_CHAR(SUM (amount_sold), '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries, products
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id AND
sales.channel_id= channels.channel_id AND sales.prod_id=products.prod_id AND
customers.country_id=countries.country_id AND channels.channel_desc IN
('Direct Sales', 'Internet') AND times.calendar_month_desc IN
('2000-09', '2000-10') AND prod_name IN ('Envoy Ambassador',
'Mouse Pad') AND countries.country_iso_code IN ('GB', 'US')
GROUP BY ROLLUP(calendar_year, calendar_quarter_desc, calendar_month_desc),
ROLLUP(country_region, country_subregion, countries.country_iso_code,
cust_state_province, cust_city),
ROLLUP(prod_category_desc, prod_subcategory_desc, prod_name);

GROUP BY指定のロールアップにより、ディメンションごとに4つずつ、次のグループが生成されます。

表21-4 階層的CUBEの例

時間別ROLLUP

製品別ROLLUP

地理別ROLLUP

year、quarter、month

category、subcategory、name

region、subregion、country、state、city

region、subregion、country、state

region、subregion、country

year、quarter

category、subcategory

region、subregion

year

category

region

all times

all products

all geographies

前述のSQLで指定されている連結グルーピングでは、表に示したROLLUP集計を使用してクロス積が実行されます。クロス積により、階層的データ・キューブに必要な96(4×4×6)の集計グループが作成されます。96個のグルーピング・セット式を必要とするような内容を、3つのROLLUP式を使用して置き換えることには、重要なメリットがあります。つまり、簡潔なSQLはエラーの可能性が大幅に減少すること、メンテナンスがはるかに容易であること、そして問合せを大幅に最適化できることです。より多数のディメンションとレベルを持つキューブによる連結グルーピングの使用方法を指定すると、さらに大きなメリットが得られます。

階層的キューブの詳細は、「SQLでの階層的キューブの処理」を参照してください。

集計を使用する場合の考慮点

この項の内容は、次のとおりです。

- ROLLUPおよびCUBEでの階層処理

- ROLLUPおよびCUBEでの列の容量

- GROUP BYの拡張機能とともに使用するHAVING句

- GROUP BYの拡張機能とともに使用するORDER BY句

- ROLLUPおよびCUBEとともに他の集計関数を使用する場合

ROLLUPおよびCUBEでの階層処理

ROLLUPおよびCUBEは、システムにあるどの階層メタデータからも独立して動作します。計算は、主にそれらを使用するSELECT文で指定された列を基にして実行されます。この方法では、階層メタデータを使用できるかどうかにかかわらず、CUBEおよびROLLUPが使用可能になります。階層ディメンションでレベルを処理するには、ROLLUPを使用し、別の列を使用して明示的にレベルを示すことが最も簡単な方法です。次に、簡単な例を示します。この例では、月は四半期にロールアップされ、四半期は年にロールアップされます。

例21-14 ROLLUPおよびCUBEでの階層処理

SELECT calendar_year, calendar_quarter_number,
calendar_month_number, SUM(amount_sold)
FROM sales, times, products, customers, countries
WHERE sales.time_id=times.time_id
AND sales.prod_id=products.prod_id
AND customers.country_id = countries.country_id
AND sales.cust_id=customers.cust_id
AND prod_name IN ('Envoy Ambassador', 'Mouse Pad')
AND country_iso_code = 'GB' AND calendar_year=1999
GROUP BY ROLLUP(calendar_year, calendar_quarter_number, calendar_month_number);

CALENDAR_YEAR CALENDAR_QUARTER_NUMBER CALENDAR_MONTH_NUMBER SUM(AMOUNT_SOLD)
------------- ----------------------- --------------------- ----------------
1999                       1                     1          5521.34
1999                       1                     2         22232.95
1999                       1                     3         10672.63
1999                       1                               38426.92
1999                       2                     4         23658.05
1999                       2                     5          5766.31
1999                       2                     6         23939.32
1999                       2                               53363.68
1999                       3                     7         12132.18
1999                       3                     8         13128.96
1999                       3                     9         19571.96
1999                       3                                44833.1
1999                       4                    10         15752.18
1999                       4                    11          7011.21
1999                       4                    12          14257.5
1999                       4                               37020.89
1999                                                      173644.59
173644.59

ROLLUPおよびCUBEでの列の容量

CUBE、ROLLUPおよびGROUPING SETSは、GROUP BY句の列容量を制限しません。GROUP BY句で処理できる列は、拡張機能使用の有無にかかわらず255列以内です。ただし、CUBEでは組合せの数が膨大になるため、CUBEによる拡張で多数の列を指定することは望ましくありません。CUBEに対する20列のリストで、結果セットに2から20の組合せが作成されたとします。膨大なCUBEリストは、システム・リソースを極限まで使用するため、そのような問合せでは、パフォーマンスおよびシステムにかかる負荷を慎重にテストする必要があります。

GROUP BYの拡張機能とともに使用するHAVING句

SELECT文のHAVING句は、GROUP BYの使用による影響を受けません。HAVING句で指定する条件は、結果セットの小計行および小計以外の行の両方に適用されます。問合せでHAVING句から小計行または小計以外の行を排除する必要がある場合もあります。これは、HAVING句とともにGROUPINGまたはGROUPING_ID関数を使用することによって可能になります。この例については、例21-8および関連するSQLを参照してください。

GROUP BYの拡張機能とともに使用するORDER BY句

多くの場合、問合せでは行を特定の方法で順序付けする必要があり、これはORDER BY句で行われます。ORDER BY句はGROUP BYの計算が完了した後に適用されるため、SELECT文のORDER BY句はGROUP BYの使用による影響を受けません。

ORDER BY指定では、結果セットの集計行と非集計行が区別されないため注意してください。たとえば、売上高を降順でリストし、各グループの最後に小計を置く必要があるとします。売上高を降順で順序付けするのみでは、小計(最大値)が各グループの最初に置かれるため不十分です。したがって、ORDER BY句の列には、集計列と非集計列を区別する列を含める必要があります。この要件は、ORDER BYをGROUP BYへの集計拡張とともに使用する問合せでは、通常、1つ以上のGROUPING関数を使用する必要があることを意味します。

ROLLUPおよびCUBEとともに他の集計関数を使用する場合

この章の例では、SUM関数で使用するROLLUPおよびCUBEを示しています。これは最も一般的な集計タイプですが、これらの拡張は、GROUP BY句で使用できるその他のすべての関数(COUNT、AVG、MIN、MAX、STDDEVおよびVARIANCE)で使用することもできます。COUNTは、クロス集計分析で必要になる場合が多く、2番目に使用頻度の高い関数と考えられます。

WITH句を使用した計算

WITH句(旧称はsubquery_factoring_clause)を使用すると、同じ問合せブロックが複雑な問合せに複数回発生する場合に、SELECT文中で再使用できます。WITHは、SQL-99標準の一部です。これは、問合せに同じ問合せブロックの参照が複数あり、結合と集計が存在する場合に特に便利です。WITH句を使用すると、Oracleでは問合せブロックの結果が取り出され、それがユーザーの一時表領域に格納されます。Oracle Databaseでは、WITH句の再帰的使用はサポートされないため注意してください。ただし、BOM(部品表)で使用する問合せや、親子階層から親子孫階層に拡張する問合せなどで使用されるWITH句の再帰的使用はサポートしています。詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

次の問合せは、WITH句を使用してパフォーマンスを改善し、SQLをより単純に記述できる一例です。この問合せでは、各チャネルの売上合計が計算され、channel_summaryという名前で保持されます。次に、各チャネルの総売上がチェックされ、総売上の3分の1を超えているチャネルの売上があるかどうかが調べられます。WITH句を使用すると、channel_summaryデータは1回のみ計算され、大きいsales表の余分なスキャンを回避できます。

例21-15 WITH句

WITH channel_summary AS (SELECT channels.channel_desc, SUM(amount_sold)
AS channel_total FROM sales, channels
WHERE sales.channel_id = channels.channel_id GROUP BY channels.channel_desc)
SELECT channel_desc, channel_total
FROM channel_summary WHERE channel_total > (SELECT SUM(channel_total) * 1/3
FROM channel_summary);

CHANNEL_DESC         CHANNEL_TOTAL
-------------------- -------------
Direct Sales            57875260.6

この例は、第22章「分析計算およびレポート用SQL関数」で説明する集計レポート関数を使用しても、効率的に実行できます。

SQLでの階層的キューブの処理

この項では、階層的キューブの処理の例を示します。

SQLでの階層的キューブの指定

Oracle Databaseでは、簡単で効率的なSQL問合せで階層的キューブを指定できます。このような階層的キューブは、多くの分析用SQL製品で論理キューブと呼ばれているものと同じです。データを階層的キューブ形式で指定するには、GROUP BY句に対する拡張機能の1つである連結グルーピング・セットを使用して、階層的データ・キューブに必要な集計を生成できます。連結ロールアップ(各ディメンションの階層に沿ってロールアップしてから、複数のディメンションにまたがってそのデータを連結すること)を使用すると、階層的キューブに必要な集計をすべて生成できます。

例21-16 連結ROLLUP

2次元の例(例21-13と同様)の階層的キューブの作成に必要なGROUP BY句は、次のとおりです。次の簡単な構文で連結ロールアップが実行されます。

GROUP BY ROLLUP(year, quarter, month), ROLLUP(Division, brand, item)

この連結ロールアップでは、表21-4「階層的CUBEの例」にリストされているROLLUP集計を使用してクロス積を実行します。クロス積により、階層的データ・キューブに必要な16(4×4)の集計グループが作成されます。

SQLでの階層的キューブの問合せ

分析アプリケーションではデータをキューブとして扱いますが、必要なのはキューブの特定のスライスおよび領域のみです。連結ロールアップ(階層的キューブ)により、リレーショナル・データをキューブとして扱えます。複雑な分析用問合せを処理する基本的な手法は、キューブの中の必要なスライスを正確に指定する外側の問合せ内に、階層的キューブ問合せを入れるというものです。Oracle Databaseでは、スライス問合せの中にネストされている階層キューブの処理を最適化します。強力なアルゴリズムを多数適用することにより、今までにないような速度と規模でこのような問合せを処理できます。これにより、SQLの分析ツールや分析アプリケーションで一貫した問合せスタイルを使用して、非常に複雑な問合せでも処理できます。

例21-17 階層的キューブの問合せ

次の分析問合せを考えてみます。この問合せは、スライス問合せの中にネストされている階層的キューブ問合せで構成されています。

SELECT month, division, sum_sales FROM
(SELECT year, quarter, month, division, brand, item, SUM(sales) sum_sales,
GROUPING_ID(grouping-columns) gid
FROM sales, products, time
WHERE join-condition
GROUP BY ROLLUP(year, quarter, month),
ROLLUP(division, brand, item))
WHERE division = 25 AND month = 200201 AND gid = gid-for-Division-Month;

内側に指定されている階層的キューブは、ディメンションが2つと各ディメンションにレベルが4つ含まれる単純なキューブを定義しています。これにより、16のグループ(4つの時間レベル×4つの製品レベル)が生成されます。問合せ内のGROUPING_ID関数は、引数内のgrouping-columnsの集計レベルに基づいて、各行が属するグループを識別します。

外側の問合せは、この問合せに必要な制約を適用し、Divisionを値25に、Monthを値200201(この場合は2002年1月を表す)に限定します。概念的には、この問合せはキューブからデータの小さいかたまりをスライスし(切り取り)ます。GID列に対する外側の問合せの制約(問合せでgid-for-division-monthにより示されている)は、データがdivisionとmonthとの組合せとしてグループ化されていることを示すキーの値です。GID制約により、monthとdivisionというGROUP BY句のレベルで集計された行のみが選択されます。

Oracle Databaseでは、外側の問合せの条件に基づき、問合せ処理から不要な集計グループが排除されます。前述の外側の問合せの条件により、結果セットはdivisionおよびmonthを集計する1つのグループに限定されます。year、month、brandおよびitemを含むその他のグループは、ここではすべて不要です。グループ・プルーニング最適化ではこれを認識し、この問合せを次のように変換します。

SELECT month, division, sum_sales
FROM (SELECT  null, null,  month, division, null, null, SUM(sales) sum_sales,
GROUPING_ID(grouping-columns) gid
FROM sales, products, time WHERE join-condition
GROUP BY month, division)
WHERE division = 25 AND month = 200201 AND gid = gid-for-Division-Month;

太字の部分が変更された箇所を示します。これで、内側の問合せには、monthとdivisionを含む単純なGROUP BY句が含まれます。year列、quarter列、brand列およびitem列は、単純化されたGROUP BY句に合うようにNULLに変換されています。これで、問合せではグループを1つのみ要求するので、16個のグループのうち15個が処理から除外され、処理量が大幅に削減されます。より多くのディメンションとレベルを持つキューブでは、グループ・プルーニングによる節約はさらに大きくなる可能性があります。グループ・プルーニング変換処理は、GROUP BY句のすべての拡張機能(ROLLUP、CUBEおよびGROUPING SETS)に適用されます。

オプティマイザにより前述の問合せは単純なGROUP BYに変換されましたが、グループが事前計算されてマテリアライズド・ビューに格納されていると、応答時間をさらに高速化できます。OLAP問合せではキューブの任意のスライスを求めることがあるため、多数のグループを事前に計算してマテリアライズド・ビューに格納しておくことが必要になります。これは次の項で説明します。

階層的キューブを格納するマテリアライズド・ビューを作成するSQL

分析用SQLでは、複数ユーザーに対する応答時間が速いことが要求されます。そのため、キューブの大部分を事前に計算してマテリアライズド・ビューに保持する必要があります。

データ・ウェアハウス設計者は、マテリアライズド・ビューのデータ量を正確に選択できます。データ・ウェアハウスでは、階層的キューブ全体を完全にマテリアライズド・ビューにして保持できます。その場合は記憶領域の量が最も多くなりますが、キューブ内のすべての問合せにすばやく応答できます。または、部分的にマテリアライズド・ビューにしたものをデータ・ウェアハウスに格納することもできます。この場合、記憶領域は節約されますが、高速に応答できるのは問合せ全体の一部のみに限定されます。問合せが、データセットで考えられるすべてのレベルの集計グループを対象としている場合は、階層キューブ全体をマテリアライズするのが最適の方法となることもあります。

これは、各ディメンションの集計階層が他の各ディメンションと組み合せて事前に計算されることを意味します。したがって、階層キューブ全体を事前に計算するには、小さい集計グループの集合より多くのディスク領域が必要であり、作成やリフレッシュの回数も増えます。処理時間およびディスク領域と問合せパフォーマンスとのトレードオフについて、作成を決定する前に考慮する必要があります。また、ディスク領域要件を少なくするためにデータ圧縮の使用も検討します。

関連項目:

- 表の圧縮の構文および制限の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

- 表の圧縮の詳細は、『Oracle Database管理者ガイド』を参照してください。

- 表の圧縮の詳細は、「記憶域および表の圧縮」を参照してください。

階層的キューブのマテリアライズド・ビューの例

この項では、完全な階層的キューブおよび部分的な階層的キューブのマテリアライズド・ビューを示します。例の多くは、機能を示すもので、実際に実行されるものではありません。

ローリング・ウィンドウを使用することが多いデータ・ウェアハウスでは、複数のマテリアライズド・ビューに対し、必要な時間のレベルごとに1つずつの階層的キューブを格納してください。これにより、完全な階層的キューブは、sales_hierarchical_mon_cube_mv、sales_hierarchical_qtr_cube_mv、sales_hierarchical_yr_cube_mvおよびsales_hierarchical_all_cube_mvという4つのマテリアライズド・ビューに格納されることになります。

次の文では、3つのコンポジット・パーティション化されたマテリアライズド・ビューと1つのリスト・パーティション化されたマテリアライズド・ビューのセットに、完全な階層的キューブを作成します。

例21-18 完全な階層的キューブのマテリアライズド・ビュー

CREATE MATERIALIZED VIEW sales_hierarchical_mon_cube_mv
PARTITION BY RANGE (mon)
SUBPARTITION BY LIST (gid)
REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT calendar_year yr, calendar_quarter_desc qtr, calendar_month_desc mon,
country_id, cust_state_province, cust_city,
prod_category, prod_subcategory, prod_name,
GROUPING_ID(calendar_year, calendar_quarter_desc, calendar_month_desc,
country_id, cust_state_province, cust_city,
prod_category, prod_subcategory, prod_name) gid,
SUM(amount_sold) s_sales, COUNT(amount_sold) c_sales,
COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id AND s.time_id = t.time_id
GROUP BY calendar_year, calendar_quarter_desc, calendar_month_desc,
ROLLUP(country_id, cust_state_province, cust_city),
ROLLUP(prod_category, prod_subcategory, prod_name),
...;

CREATE MATERIALIZED VIEW sales_hierarchical_qtr_cube_mv
REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT calendar_year yr, calendar_quarter_desc qtr,
country_id, cust_state_province, cust_city,
prod_category, prod_subcategory, prod_name,
GROUPING_ID(calendar_year, calendar_quarter_desc,
country_id, cust_state_province, cust_city,
prod_category, prod_subcategory, prod_name) gid,
SUM(amount_sold) s_sales, COUNT(amount_sold) c_sales,
COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id
AND s.time_id = t.time_id
GROUP BY calendar_year, calendar_quarter_desc,
ROLLUP(country_id, cust_state_province, cust_city),
ROLLUP(prod_category, prod_subcategory, prod_name),
PARTITION BY RANGE (qtr)
SUBPARTITION BY LIST (gid)
...;

CREATE MATERIALIZED VIEW sales_hierarchical_yr_cube_mv
PARTITION BY RANGE (year)
SUBPARTITION BY LIST (gid)
REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT calendar_year yr, country_id, cust_state_province, cust_city,
prod_category, prod_subcategory, prod_name,
GROUPING_ID(calendar_year, country_id, cust_state_province, cust_city,
prod_category, prod_subcategory, prod_name) gid,
SUM(amount_sold) s_sales, COUNT(amount_sold) c_sales, COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id AND s.time_id = t.time_id
GROUP BY calendar_year,
ROLLUP(country_id, cust_state_province, cust_city),
ROLLUP(prod_category, prod_subcategory, prod_name),
...;

CREATE MATERIALIZED VIEW sales_hierarchical_all_cube_mv
REFRESH FAST ON DEMAND
ENABLE QUERY REWRITE AS
SELECT country_id, cust_state_province, cust_city,
prod_category, prod_subcategory, prod_name,
GROUPING_ID(country_id, cust_state_province, cust_city,
prod_category, prod_subcategory, prod_name) gid,
SUM(amount_sold) s_sales, COUNT(amount_sold) c_sales, COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id AND s.time_id = t.time_id
GROUP BY ROLLUP(country_id, cust_state_province, cust_city),
ROLLUP(prod_category, prod_subcategory, prod_name),
PARTITION BY LIST (gid)
...;

これにより、sales表に対するパーティション・メンテナンス操作の際に、sales_hierarchical_mon_cube_mv、sales_hierarchical_qtr_cube_mvおよびsales_hierarchical_yr_cube_mvの各マテリアライズド・ビューでPCTリフレッシュを使用できるようになります。実表に大幅な変更があり、ログ・ベースの高速リフレッシュがPCTリフレッシュよりも遅くなると予想される場合にも、PCTリフレッシュを使用できます。FORCEメソッド(method => '?')を指定して、DBMS_MVIEWパッケージのリフレッシュ・サブプログラムを実行すると、Oracle Databaseはリフレッシュに最適な方法を選択します。PCTリフレッシュの詳細は、「パーティション・チェンジ・トラッキング(PCT)リフレッシュ」を参照してください。

sales_hierarchical_qtr_cube_mvにはtimes表の列は含まれないため、このマテリアライズド・ビューではPCTリフレッシュは有効になりません。ただし、FORCEメソッド(method => '?')を指定すれば、DBMS_MVIEWパッケージのリフレッシュ・サブプログラムをコールできます。Oracle Databaseは、リフレッシュに最適な方法を選択します。

部分的なキューブ(つまり、完全なキューブからのグルーピングのサブセット)が必要な場合は、キューブを「連合キューブ」として格納することをお薦めします。連合キューブは、必要な各グルーピングを別個のマテリアライズド・ビューに格納します。

CREATE MATERIALIZED VIEW sales_mon_city_prod_mv
PARTITION BY RANGE (mon)
...
BUILD DEFERRED
REFRESH FAST ON DEMAND
USING TRUSTED CONSTRAINTS
ENABLE QUERY REWRITE AS
SELECT calendar_month_desc mon, cust_city, prod_name, SUM(amount_sold) s_sales,
COUNT(amount_sold) c_sales, COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id
AND s.time_id = t.time_id
GROUP BY calendar_month_desc, cust_city, prod_name;

CREATE MATERIALIZED VIEW sales_qtr_city_prod_mv
PARTITION BY RANGE (qtr)
...
BUILD DEFERRED
REFRESH FAST ON DEMAND
USING TRUSTED CONSTRAINTS
ENABLE QUERY REWRITE AS
SELECT calendar_quarter_desc qtr, cust_city, prod_name,SUM(amount_sold) s_sales,
COUNT(amount_sold) c_sales, COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id =p.prod_id AND s.time_id = t.time_id
GROUP BY calendar_quarter_desc, cust_city, prod_name;

CREATE MATERIALIZED VIEW sales_yr_city_prod_mv
PARTITION BY RANGE (yr)
...
BUILD DEFERRED
REFRESH FAST ON DEMAND
USING TRUSTED CONSTRAINTS
ENABLE QUERY REWRITE AS
SELECT calendar_year yr, cust_city, prod_name, SUM(amount_sold) s_sales,
COUNT(amount_sold) c_sales, COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id =p.prod_id AND s.time_id = t.time_id
GROUP BY calendar_year, cust_city, prod_name;

CREATE MATERIALIZED VIEW sales_mon_city_scat_mv
PARTITION BY RANGE (mon)
...
BUILD DEFERRED
REFRESH FAST ON DEMAND
USING TRUSTED CONSTRAINTS
ENABLE QUERY REWRITE AS
SELECT calendar_month_desc mon, cust_city, prod_subcategory,
SUM(amount_sold) s_sales, COUNT(amount_sold) c_sales, COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id =p.prod_id AND s.time_id =t.time_id
GROUP BY calendar_month_desc, cust_city, prod_subcategory;

CREATE MATERIALIZED VIEW sales_qtr_city_cat_mv
PARTITION BY RANGE (qtr)
...
BUILD DEFERRED
REFRESH FAST ON DEMAND
USING TRUSTED CONSTRAINTS
ENABLE QUERY REWRITE AS
SELECT calendar_quarter_desc qtr, cust_city, prod_category cat,
SUM(amount_sold) s_sales, COUNT(amount_sold) c_sales, COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id =p.prod_id AND s.time_id =t.time_id
GROUP BY calendar_quarter_desc, cust_city, prod_category;

CREATE MATERIALIZED VIEW sales_yr_city_all_mv
PARTITION BY RANGE (yr)
...
BUILD DEFERRED
REFRESH FAST ON DEMAND
USING TRUSTED CONSTRAINTS
ENABLE QUERY REWRITE AS
SELECT calendar_year yr, cust_city, SUM(amount_sold) s_sales,
COUNT(amount_sold) c_sales, COUNT(*) c_star
FROM sales s, products p, customers c, times t
WHERE s.cust_id = c.cust_id AND s.prod_id = p.prod_id AND s.time_id = t.time_id
GROUP BY calendar_year, cust_city;

これらのマテリアライズド・ビューは、BUILD DEFERREDとして作成でき、次にDBMS_MVIEW.REFRESH_DEPENDENT(number_of_failures, 'SALES', 'C' ...)を実行すると、ディテール表salesで定義されている各マテリアライズド・ビューの完全リフレッシュが、最も効率的な順序でスケジューリングされます。詳細は、「リフレッシュのスケジューリング」を参照してください。

各マテリアライズド・ビューは、SELECT構文のリストで時間レベル(月、四半期、年)にパーティション化されるので、PCTは各マテリアライズド・ビューのsales表で使用できます。これによって、FASTおよびCOMPLETEリフレッシュ方法に加え、PCTリフレッシュ方法も適用できます。

22 分析計算およびレポート用SQL関数

この章では、データ・ウェアハウスにおける分析SQL問合せの改善方法について説明します。内容は次のとおりです。

- 分析計算およびレポート用SQL関数の概要

- ランキング関数、ウィンドウ関数およびレポート関数

- 分析用の高度な集計

- ピボット操作

- レポート用のデータの稠密化

- 稠密化したデータに対する時系列の計算

- その他の分析およびレポートの機能

分析計算およびレポート用SQL関数の概要

Oracleでは、新しい分析用SQL関数ファミリの導入により、SQLの分析処理機能が強化されています。このような分析用の関数を使用すると、次の計算が可能です。

- ランキングとパーセンタイル(百分位数)

- 変動ウィンドウの計算

- LAG/LEAD分析

- FIRST/LAST分析

- 線形回帰統計

ランキング関数には、累積分散、パーセント・ランクおよびNタイルなどがあります。変動ウィンドウの計算では、合計や平均などの変動集計および累積集計の検索が可能になります。LAG/LEAD分析では、行間の直接参照が可能になるため、周期ごとの変更を計算できます。FIRST/LAST分析では、順序付けされたグループ内の最初の値または最後の値を検索できます。

SQLに対するその他の拡張には、CASE式とパーティション外部結合があります。CASE式では、様々な状況で有効なif-thenロジックが提供されます。パーティション外部結合は、特定のディメンションを選択的に稠密化しながら、その他のディメンションをスパースなままにできるANSI外部結合構文の拡張です。これにより、たとえば、その他のディメンションはスパースなままで、クロス集計レポートに表示されるディメンションをレポート・ツールで選択的に稠密化できます。

パフォーマンスを向上させるには、分析関数をパラレル化する方法があります。つまり、複数のプロセスですべての文を同時に実行できます。こうした機能によって計算がより容易かつ効率的になるので、データベースのパフォーマンス、スケーラビリティおよび簡易性が向上します。

分析関数は、表22-1に示すように分類されます。

表22-1 分析関数およびその使用目的

種類

使用目的

ランキング

結果セットのランク、パーセンタイルおよびnタイルの値を計算する。

ウィンドウ

累積集計および変動集計を計算する。SUM、AVG、MIN、MAX、COUNT、VARIANCE、STDDEV、FIRST_VALUE、LAST_VALUEおよび新しい統計関数とともに動作する。DISTINCTキーワードはMAXおよびMINを除くウィンドウ関数ではサポートされません。

レポート

市場占有率などのシェアを計算する。SUM、AVG、MIN、MAX、COUNT(DISTINCT付き/なし)、VARIANCE、STDDEV、RATIO_TO_REPORTおよび新しい統計関数とともに動作する。集計モードでDISTINCTキーワードがサポートされるレポート関数では、DISTINCTを使用できる。

LAG/LEAD

現在行から指定した行数を移動した行の値を検索する。

FIRST/LAST

順序付けされたグループ内の最初または最後の値。

線形回帰

線形回帰およびその他の統計情報(傾き、切片など)を計算する。

逆パーセンタイル

データセット内で指定されたパーセンタイルと一致する値。

仮説ランクおよび仮説分布

行が指定されたデータセットに挿入された場合に与えられるランクまたはパーセンタイル。

こうした処理を行うため、分析関数ではSQL処理に新しい要素がいくつか追加されています。これらの要素は、既存のSQL上に作成され、柔軟で強力な計算式を可能にします。分析関数には、いくつかの例外を除きこの新しい要素群が含まれます。図22-1に、処理フローを表します。

図22-1 処理順序

次に、分析関数における重要な概念を示します。

- 処理順序
分析関数を使用した問合せ処理は、3つのステップで実行されます。第1に、すべての結合、WHERE、GROUP BYおよびHAVING句が実行されます。第2に、結果セットを分析関数で使用できるようになり、その計算がすべて実行されます。第3に、問合せの最後にORDER BY句がある場合、ORDER BYが処理され、正確な出力順序付けが可能になります。処理順序は、図22-1のとおりです。

- 結果セット・パーティション
分析関数を使用すると、問合せの結果セットをパーティションと呼ばれる行グループに分割できます。分析関数で使用されるパーティションという用語は、表パーティション機能とは無関係です。この章でパーティションという用語が使用される場合、分析関数に関する意味のみを示します。パーティションはGROUP BY句で定義されているグループの後に作成されるため、SUMやAVGなどのすべての集計結果で使用できます。パーティションは、必要な列または式に基づいて分割することもできます。問合せ結果セットは、すべての行を持つ1つのパーティション、少数の大きなパーティション、またはそれぞれが数行しか持たない多数の小さなパーティションに分割可能です。

- ウィンドウ
パーティションの各行に対して、スライドするデータ・ウィンドウを定義できます。このウィンドウで、カレント行の計算に使用される行の範囲が決まります。ウィンドウ・サイズは、物理的な行数または時間などの論理間隔に基づきます。ウィンドウには、開始行および終了行があります。ウィンドウの定義によっては、ウィンドウの片方または両方の末端を移動できます。たとえば、累積合計関数として定義されているウィンドウでは、開始行がパーティションの最初の行に固定されますが、終了行はパーティションの開始点から最後の行までスライドします。一方、移動平均として定義されているウィンドウでは、開始点および終了点の両方がスライドし、一定の物理範囲または論理範囲が維持されます。

ウィンドウのサイズは、パーティション内の行全体と同じ大きさからパーティション内の1行のスライド・ウィンドウとしてまで様々に設定できます。ウィンドウがパーティションの境界に近い場合、関数からは使用可能な行の結果のみが戻されます。結果がユーザーの必要とするものではないことは警告されません。

ウィンドウ関数を使用するとカレント行が計算に含まれるため、n個の項目を処理する場合は(n-1)を指定します。

- カレント行
分析関数を使用して実行される各計算は、パーティション内のカレント行に基づいて行われます。カレント行は、ウィンドウの開始および終了を判断する際の参照点として機能します。たとえば、中央の移動平均計算は、カレント行とその前の6行およびその後の6行にわたるウィンドウのように定義できます。この場合、図22-2のように、13行の大きさのスライド・ウィンドウが作成されます。

図22-2 スライド・ウィンドウの例

ランキング関数、ウィンドウ関数およびレポート関数

この項では、ランキング、ウィンドウおよびレポート用の基本的な分析関数について説明します。

ランキング

ランキング関数では、メジャーの集合の値に基づいたデータセット内の他のレコードとの比較により、レコードのランクが計算されます。ランキング関数の種類を次に示します。

RANK関数およびDENSE_RANK関数

RANK関数およびDENSE_RANK関数では、グループ内での項目のランク付けができます。たとえば、昨年カリフォルニアでよく売れた製品の上位3位までを検索する場合などに利用できます。次の構文で示すように、ランキングを実行する関数には2種類あります。

RANK ( ) OVER ( [query_partition_clause] order_by_clause )
DENSE_RANK ( ) OVER ( [query_partition_clause] order_by_clause )

RANKとDENSE_RANKの違いは、同じ値の項目がある場合、DENSE_RANKではランキングの順序に抜けができないという点です。つまり、ある競争についてDENSE_RANKを使用してランキングした結果、3人が同点で第2位であった場合、3人全員が第2位となり、次点の人が第3位になります。RANK関数でも3人全員が第2位になりますが、次点の人は第5位になります。

次に、RANKに関する注意点を示します。

- デフォルトのソート順は昇順です。必要に応じて降順に変更できます。

- オプションのPARTITION BY句の式を使用すると、問合せ結果セットがRANK関数の適用範囲となるグループ群に分割されます。つまり、グループが変更されるたびに、RANKがリセットされます。実際には、PARTITION BY句の値式でリセットの境界が定義されます。

- PARTITION BY句がない場合、ランクは問合せ結果セット全体にわたって計算されます。

- ORDER> BY句によって、ランキングが実行されるメジャー(<value expression>)が指定され、各グループ(またはパーティション)でソートされる行の順序が定義されます。各パーティション内でデータがソートされると、各行が1からランク付けされます。

- NULLS FIRST | NULLS LAST句によって、順序付けされた行セットでNULLの位置が最初になるか最後になるかが示されます。順序付けによって、NULLが、NULL以外の値より高いか低いかが比較されます。順序が昇順であった場合、NULLS FIRSTはNULLが他のどのNULL以外の値よりも小さいことを示し、NULLS LASTはNULL以外の値よりも大きいことを示します。降順では、その逆になります。「NULLの処理」の例を参照してください。

- NULLS FIRST | NULLS LAST句が省略されている場合、NULL値の順序付けはASC引数またはDESC引数に依存します。NULL値は、他のどの値よりも大きいとみなされます。順序付け順序がASCの場合、NULLは最後に表示されます。それ以外の場合は、最初に表示されます。NULLは他のNULLと同等とみなされるため、NULLが表示されている順序は確定的ではありません。

ランキング順序

次の例では、[ASC | DESC]オプションによるランキング順序の変化を示します。

例22-1 ランキング順序

SELECT channel_desc, TO_CHAR(SUM(amount_sold), '9,999,999,999') SALES$,
RANK() OVER (ORDER BY SUM(amount_sold)) AS default_rank,
RANK() OVER (ORDER BY SUM(amount_sold) DESC NULLS LAST) AS custom_rank
FROM sales, products, customers, times, channels, countries
WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
AND customers.country_id = countries.country_id AND sales.time_id=times.time_id
AND sales.channel_id=channels.channel_id
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND country_iso_code='US'
GROUP BY channel_desc;

CHANNEL_DESC         SALES$         DEFAULT_RANK CUSTOM_RANK
-------------------- -------------- ------------ -----------
Direct Sales              1,320,497            3           1
Partners                    800,871            2           2
Internet                    261,278            1           3

この結果のデータはメジャーSALES$で順序付けされていますが、一般にRANK関数では、データがメジャーでソートされるという保証はありません。結果のデータがSALES$でソートされるようにするには、SELECT文の最後にORDER BY句で明示的にソートを指定する必要があります。

複数の式でのランキング

ランキング関数では、セット内にある同じ内容の値について解決する必要があります。最初の式で同じ内容の値が解決されない場合、2番目の式が同じ内容の値の解決に使用され、以降同様に続きます。たとえば、売上高(ドル)に基づいて2か月間の販売チャネルのうち3つをランキングし、単位売上で同じ内容の値を解決する問合せの例を次に示します。(ここでは、この問合せ用に同じ内容の値を作成するためだけにTRUNC関数を使用しています)。

例22-2 複数の式でのランキング

SELECT channel_desc, calendar_month_desc, TO_CHAR(TRUNC(SUM(amount_sold),-5),
'9,999,999,999') SALES$, TO_CHAR(SUM(quantity_sold), '9,999,999,999')
SALES_Count, RANK() OVER (ORDER BY TRUNC(SUM(amount_sold), -5)
DESC, SUM(quantity_sold) DESC) AS col_rank
FROM sales, products, customers, times, channels
WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
AND sales.time_id=times.time_id AND sales.channel_id=channels.channel_id
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND channels.channel_desc<>'Tele Sales'
GROUP BY channel_desc, calendar_month_desc;

CHANNEL_DESC         CALENDAR SALES$         SALES_COUNT     COL_RANK
-------------------- -------- -------------- -------------- ---------
Direct Sales         2000-10       1,200,000         12,584          1
Direct Sales         2000-09       1,200,000         11,995          2
Partners             2000-10         600,000          7,508          3
Partners             2000-09         600,000          6,165          4
Internet             2000-09         200,000          1,887          5
Internet             2000-10         200,000          1,450          6

sales_count列で、3組の同じ内容の値が解決されています。

RANKとDENSE_RANKの違い

RANK関数とDENSE_RANK関数の違いを例22-3に示します。

例22-3 RANKとDENSE_RANK

SELECT channel_desc, calendar_month_desc,
TO_CHAR(TRUNC(SUM(amount_sold),-5), '9,999,999,999') SALES$,
RANK() OVER (ORDER BY TRUNC(SUM(amount_sold),-5) DESC) AS RANK,
DENSE_RANK() OVER (ORDER BY TRUNC(SUM(amount_sold),-5) DESC) AS DENSE_RANK
FROM sales, products, customers, times, channels
WHERE sales.prod_id=products.prod_id
AND sales.cust_id=customers.cust_id
AND sales.time_id=times.time_id AND sales.channel_id=channels.channel_id
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND channels.channel_desc<>'Tele Sales'
GROUP BY channel_desc, calendar_month_desc;

CHANNEL_DESC         CALENDAR SALES$              RANK DENSE_RANK
-------------------- -------- -------------- --------- ----------
Direct Sales         2000-09       1,200,000         1          1
Direct Sales         2000-10       1,200,000         1          1
Partners             2000-09         600,000         3          2
Partners             2000-10         600,000         3          2
Internet             2000-09         200,000         5          3
Internet             2000-10         200,000         5          3

DENSE_RANKでは、最大のランク値がデータセット内の個別値の数を示すことに注意します。

グループごとのランキング

RANK関数は、グループ内部を処理対象として使用できます。この場合、グループが変更されるたびに、ランクがリセットされます。これを可能にするには、PARTITION BY句を使用します。PARTITION BY副次句のグループ式を使用すると、データセットが、RANKの操作対象となるグループに分割されます。たとえば、ドル単位の売上高によって各チャネル内で製品を順序付ける場合は、次の文を発行できます。

例22-4 グループごとのランキングの例1

SELECT channel_desc, calendar_month_desc, TO_CHAR(SUM(amount_sold),
'9,999,999,999') SALES$, RANK() OVER (PARTITION BY channel_desc
ORDER BY SUM(amount_sold) DESC) AS RANK_BY_CHANNEL
FROM sales, products, customers, times, channels
WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
AND sales.time_id=times.time_id AND sales.channel_id=channels.channel_id
AND times.calendar_month_desc IN ('2000-08', '2000-09', '2000-10', '2000-11')
AND channels.channel_desc IN ('Direct Sales', 'Internet')
GROUP BY channel_desc, calendar_month_desc;

CHANNEL_DESC         CALENDAR SALES$         RANK_BY_CHANNEL
-------------------- -------- -------------- ---------------
Direct Sales         2000-08       1,236,104               1
Direct Sales         2000-10       1,225,584               2
Direct Sales         2000-09       1,217,808               3
Direct Sales         2000-11       1,115,239               4
Internet             2000-11         284,742               1
Internet             2000-10         239,236               2
Internet             2000-09         228,241               3
Internet             2000-08         215,107               4

8 rows selected.

単一の問合せブロックに、複数のランキング関数を含めることができます。これらの各関数によって、種々のグループにデータがパーティション化(種々の境界上でリセット)されます。そうしたグループは、相互に排他的にできます。次の問合せでは、各月(rank_of_product_per_region)および各チャネル(rank_of_product_total)内において、ドル単位の売上高に基づき製品が順序付けされます。

例22-5 グループごとのランキングの例2

SELECT channel_desc, calendar_month_desc, TO_CHAR(SUM(amount_sold),
'9,999,999,999') SALES$, RANK() OVER (PARTITION BY calendar_month_desc
ORDER BY SUM(amount_sold) DESC) AS RANK_WITHIN_MONTH, RANK() OVER (PARTITION
BY channel_desc ORDER BY SUM(amount_sold) DESC) AS RANK_WITHIN_CHANNEL
FROM sales, products, customers, times, channels, countries
WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
AND customers.country_id = countries.country_id AND sales.time_id=times.time_id
AND sales.channel_id=channels.channel_id
AND times.calendar_month_desc IN ('2000-08', '2000-09', '2000-10', '2000-11')
AND channels.channel_desc IN ('Direct Sales', 'Internet')
GROUP BY channel_desc, calendar_month_desc;

CHANNEL_DESC  CALENDAR    SALES$     RANK_WITHIN_MONTH  RANK_WITHIN_CHANNEL
------------- --------    ---------  -----------------  -------------------
Direct Sales   2000-08    1,236,104                  1                    1
Internet       2000-08      215,107                  2                    4
Direct Sales   2000-09    1,217,808                  1                    3
Internet       2000-09      228,241                  2                    3
Direct Sales   2000-10    1,225,584                  1                    2
Internet       2000-10      239,236                  2                    2
Direct Sales   2000-11    1,115,239                  1                    4
Internet       2000-11      284,742                  2                    1

CUBEグループおよびROLLUPグループごとのランキング

RANKなどの分析関数は、CUBE、ROLLUPまたはGROUPING SETS演算子によるグルーピングに基づいて再設定されます。これは、CUBE、ROLLUPおよびGROUPING SETS問合せで作成されたグループにランクを割り当てる場合に有効です。GROUPING関数の詳細は、第21章「データ・ウェアハウスにおける集計のためのSQL」を参照してください。

CUBEおよびROLLUP問合せの例を次に示します。

SELECT channel_desc, country_iso_code,
TO_CHAR(SUM(amount_sold), '9,999,999,999')
SALES$, RANK() OVER (PARTITION BY GROUPING_ID(channel_desc, country_iso_code)
ORDER BY SUM(amount_sold) DESC) AS RANK_PER_GROUP
FROM sales, customers, times, channels, countries
WHERE sales.time_id=times.time_id AND sales.cust_id=customers.cust_id
AND sales.channel_id = channels.channel_id AND channels.channel_desc
IN ('Direct Sales', 'Internet') AND times.calendar_month_desc='2000-09'
AND country_iso_code IN ('GB', 'US', 'JP')
GROUP BY CUBE(channel_desc, country_iso_code);

CHANNEL_DESC         CO SALES$         RANK_PER_GROUP
-------------------- -- -------------- --------------
Direct Sales         GB      1,217,808              1
Direct Sales         JP      1,217,808              1
Direct Sales         US      1,217,808              1
Internet             GB        228,241              4
Internet             JP        228,241              4
Internet             US        228,241              4
Direct Sales                 3,653,423              1
Internet                       684,724              2
GB      1,446,049              1
JP      1,446,049              1
US      1,446,049              1
4,338,147              1

NULLの処理

NULLは、通常の値と同様に処理されます。また、ランクの計算では、NULL値は別のNULL値と同等であると想定されています。NULLは、メジャーに設定したASC | DESCオプション、およびNULLS FIRST | NULLS LAST句に従って高低にソートされ、適切にランク付けされます。次の例では、NULLが様々なケースにおいてどのようにランク付けされるかを示します。

SELECT  times.time_id time,  sold,
RANK() OVER (ORDER BY (sold) DESC NULLS LAST) AS NLAST_DESC,
RANK() OVER (ORDER BY (sold) DESC NULLS FIRST) AS NFIRST_DESC,
RANK() OVER (ORDER BY (sold) ASC NULLS FIRST) AS NFIRST,
RANK() OVER (ORDER BY (sold) ASC NULLS LAST) AS NLAST
FROM
(
SELECT time_id, SUM(sales.amount_sold)  sold
FROM sales, products, customers, countries
WHERE sales.prod_id=products.prod_id
AND customers.country_id = countries.country_id
AND sales.cust_id=customers.cust_id
AND prod_name IN ('Envoy Ambassador', 'Mouse Pad') AND country_iso_code ='GB'
GROUP BY  time_id)
v, times
WHERE v.time_id (+) = times.time_id
AND calendar_year=1999
AND calendar_month_number=1
ORDER BY sold  DESC NULLS LAST;

TIME            SOLD NLAST_DESC NFIRST_DESC     NFIRST      NLAST
--------- ---------- ---------- ----------- ---------- ----------
25-JAN-99    3097.32          1          18         31         14
17-JAN-99    1791.77          2          19         30         13
30-JAN-99     127.69          3          20         29         12
28-JAN-99     120.34          4          21         28         11
23-JAN-99      86.12          5          22         27         10
20-JAN-99      79.07          6          23         26          9
13-JAN-99       56.1          7          24         25          8
07-JAN-99      42.97          8          25         24          7
08-JAN-99      33.81          9          26         23          6
10-JAN-99      22.76         10          27         21          4
02-JAN-99      22.76         10          27         21          4
26-JAN-99      19.84         12          29         20          3
16-JAN-99      11.27         13          30         19          2
14-JAN-99       9.52         14          31         18          1
09-JAN-99                    15           1          1         15
12-JAN-99                    15           1          1         15
31-JAN-99                    15           1          1         15
11-JAN-99                    15           1          1         15
19-JAN-99                    15           1          1         15
03-JAN-99                    15           1          1         15
15-JAN-99                    15           1          1         15
21-JAN-99                    15           1          1         15
24-JAN-99                    15           1          1         15
04-JAN-99                    15           1          1         15
06-JAN-99                    15           1          1         15
27-JAN-99                    15           1          1         15
18-JAN-99                    15           1          1         15
01-JAN-99                    15           1          1         15
22-JAN-99                    15           1          1         15
29-JAN-99                    15           1          1         15
05-JAN-99                    15           1          1         15

ボトムNランキング

ボトムNはトップNに似ていますが、ランク式内の順序付け順序が異なります。前述の例の場合でいうと、降順のかわりに昇順でSUM(s_amount)を順序付けできます。

CUME_DIST関数

CUME_DIST関数(統計書によっては、パーセンタイルの逆と定義されている関数)によって、値の集合に対する特定の値の相対位置が計算されます。この順序は、昇順または降順にできます。デフォルトは昇順です。CUME_DISTで戻される値の範囲は、0(ゼロ)から1です。サイズNの集合Sに含まれる値XのCUME_DISTを計算するには、次の計算式を使用します。

CUME_DIST(x) =  number of values in S coming before
and including x in the specified order/ N

構文は次のとおりです。

CUME_DIST ( ) OVER ( [query_partition_clause] order_by_clause )

CUME_DIST関数の様々なオプションは、意味的にはRANK関数のオプションと同様です。デフォルトの順序は昇順であり、最小値が最小のCUME_DISTを取得します(他のすべての値はこの値の後に順序付けされます)。NULLは、RANK関数の場合と同様に処理されます。NULL以外の値と同様に処理されるため、分子および分母の両方が考慮されます。次の例では、各月におけるチャネル別の売上の累積分布がわかります。

SELECT calendar_month_desc AS MONTH, channel_desc,
TO_CHAR(SUM(amount_sold) , '9,999,999,999') SALES$,
CUME_DIST() OVER (PARTITION BY calendar_month_desc ORDER BY
SUM(amount_sold) ) AS CUME_DIST_BY_CHANNEL
FROM sales, products, customers, times, channels
WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
AND sales.time_id=times.time_id AND sales.channel_id=channels.channel_id
AND times.calendar_month_desc IN ('2000-09', '2000-07','2000-08')
GROUP BY calendar_month_desc, channel_desc;

MONTH    CHANNEL_DESC         SALES$         CUME_DIST_BY_CHANNEL
-------- -------------------- -------------- --------------------
2000-07  Internet                    140,423           .333333333
2000-07  Partners                    611,064           .666666667
2000-07  Direct Sales              1,145,275                    1
2000-08  Internet                    215,107           .333333333
2000-08  Partners                    661,045           .666666667
2000-08  Direct Sales              1,236,104                    1
2000-09  Internet                    228,241           .333333333
2000-09  Partners                    666,172           .666666667
2000-09  Direct Sales              1,217,808                    1

PERCENT_RANK関数

PERCENT_RANKはCUME_DISTと似ていますが、分子に行カウントではなくランク値が使用されます。したがって、PERCENT_RANKでは値グループに対する値の相対的なパーセント・ランクが戻されます。この関数は、一般的なスプレッドシートで使用できます。ある行のPERCENT_RANKは次のように計算されます。

(rank of row in its partition - 1) / (number of rows in the partition - 1)

PERCENT_RANKでは、0(ゼロ)から1の範囲の値が戻されます。ランク1の行は、PERCENT_RANKが0(ゼロ)になります。構文は次のとおりです。

PERCENT_RANK () OVER ([query_partition_clause] order_by_clause)

NTILE関数

NTILEを使用すると、三分位数、四分位数、十分位数およびその他の一般的な集計統計情報を簡単に計算できます。この関数では、順序付けられたパーティションがバケットと呼ばれる特定数のグループに分割され、バケット番号がパーティションの各行に割り当てられます。NTILE計算は、データセットを4分割、3分割およびその他のグループ数に分割できるため、非常に便利です。

各バケットは、それぞれに同数の行が割り当てられるか、他のバケットとの差が最大でも1行となるように計算されます。たとえば、パーティションに100の行があって、バケットが4つになるようにNTILE関数に要求した場合、最初の25行に1の値、次の25行に2の値が割り当てられ、残りも同様に割り当てられます。これらのバケットは、等度数バケットと呼ばれます。

パーティションの行数が所定のバケット数に等分に(余りなく)分割されない場合、各バケットに割り当てられる行数には最大で1行の差ができます。余りの行は、バケット番号が最小のバケットから順に、バケットごとに1行ずつ分配されます。たとえば、NTILE(5)関数を持つパーティションに103の行がある場合、最初の21行は第1バケットに、次の21行は第2バケットに、次の21行は第3バケットに、次の20行は第4バケットに、最後の20行は第5バケットに分割されます。

NTILE関数の構文は次のとおりです。

NTILE (expr) OVER ([query_partition_clause] order_by_clause)

NTILE(N)のNは、定数(5など)または式になります。

この関数では、RANKやCUME_DISTと同様に、グループごとの計算用にPARTITION BY句、メジャーおよびそのソート順序の指定用にORDER BY句、および特定のNULL処理用にNULLS FIRST | NULLS LAST句を使用できます。例として、各月の総売上を4つのバケットのそれぞれに割り当てる場合を次に示します。

SELECT calendar_month_desc AS MONTH , TO_CHAR(SUM(amount_sold),
'9,999,999,999')
SALES$, NTILE(4) OVER (ORDER BY SUM(amount_sold)) AS TILE4
FROM sales, products, customers, times, channels
WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
AND sales.time_id=times.time_id AND sales.channel_id=channels.channel_id
AND times.calendar_year=2000 AND prod_category= 'Electronics'
GROUP BY calendar_month_desc;

MONTH    SALES$              TILE4
-------- -------------- ----------
2000-02         242,416          1
2000-01         257,286          1
2000-03         280,011          1
2000-06         315,951          2
2000-05         316,824          2
2000-04         318,106          2
2000-07         433,824          3
2000-08         477,833          3
2000-12         553,534          3
2000-10         652,225          4
2000-11         661,147          4
2000-09         691,449          4

再生可能な結果を得るには、NTILE ORDER BY文を完全に指定する必要があります。同等の値は、隣接バケット間で分散されます。確定的な結果を確実に得るには、一意キーで順序付けを行う必要があります。

ROW_NUMBER関数

ROW_NUMBER関数では、(ORDER BYで定義されたとおり、1から順番に)一意の番号がパーティション内の各行に割り当てられます。構文は次のとおりです。

ROW_NUMBER ( ) OVER ( [query_partition_clause] order_by_clause )

例22-6 ROW_NUMBER

SELECT channel_desc, calendar_month_desc,
TO_CHAR(TRUNC(SUM(amount_sold), -5), '9,999,999,999') SALES$,
ROW_NUMBER() OVER (ORDER BY TRUNC(SUM(amount_sold), -6) DESC) AS ROW_NUMBER
FROM sales, products, customers, times, channels
WHERE sales.prod_id=products.prod_id AND sales.cust_id=customers.cust_id
AND sales.time_id=times.time_id AND sales.channel_id=channels.channel_id
AND times.calendar_month_desc IN ('2001-09', '2001-10')
GROUP BY channel_desc, calendar_month_desc;

CHANNEL_DESC         CALENDAR SALES$         ROW_NUMBER
-------------------- -------- -------------- ----------
Direct Sales         2001-10       1,000,000          1
Direct Sales         2001-09       1,100,000          2
Internet             2001-09         500,000          3
Partners             2001-09         600,000          4
Partners             2001-10         600,000          5
Internet             2001-10         700,000          6

上述の結果には同等の値のペアが3組あります。NTILEと同様、ROW_NUMBERも不確定的な関数なので、同じ内容の値のそれぞれにおいて行番号が変わる場合があります。確定的な結果を確実に得るには、一意キーで順序付けを行う必要があります。ほとんどの場合、同じ内容の値を解決する列を新しく問合せに追加し、それをORDER BYの指定で使用する必要があります。

ウィンドウ

一連のウィンドウ関数を使用すると、累積集計、移動集計および集中集計を計算できます。この種類の関数では、対応するウィンドウ内にある他の行に基づいて、表内の各行に対する値が戻されます。集計ウィンドウ関数群では、SUM、AVERAGE、COUNT、MAX、MINを始めとする多数の関数の移動および累積バージョンの計算が可能です。この種の関数は、問合せのSELECT句およびORDER BY句でのみ使用できます。集計ウィンドウ関数群には、ウィンドウ内の最初の値が戻されるFIRST_VALUE、ウィンドウ内の最後の値が戻されるLAST_VALUEといった便利な関数があります。これらの関数を使用すると、自己結合なしで表の複数の行にアクセスできます。ウィンドウ関数の構文は次のとおりです。

analytic_function([ arguments ])
OVER (analytic_clause)

where analytic_clause =
[ query_partition_clause ]
[ order_by_clause [ windowing_clause ] ]

and query_partition_clause =
PARTITION BY
{ value_expr[, value_expr ]...
}

and windowing_clause =
{ ROWS | RANGE }
{ BETWEEN
{ UNBOUNDED PRECEDING
| CURRENT ROW
| value_expr { PRECEDING | FOLLOWING }
}
AND
{ UNBOUNDED FOLLOWING
| CURRENT ROW
| value_expr { PRECEDING | FOLLOWING }
}
| { UNBOUNDED PRECEDING
| CURRENT ROW
| value_expr PRECEDING
}
}

DISTINCTキーワードはMAXおよびMINを除くウィンドウ関数ではサポートされません。

参照:
構文および制限の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

ウィンドウ関数に入力したNULLの取扱

ウィンドウ関数のNULLの処理方法は、SQL集計関数のNULLの処理方法と同じです。その他の処理方法は、ユーザー定義の関数によって、またはウィンドウ関数でDECODEかCASE式を使用することによって取得できます。

論理オフセットを指定したウィンドウ関数

論理オフセットは、RANGE 10 PRECEDINGなどの定数または定数を求める式で指定するか、RANGE INTERVAL N DAY/MONTH/YEAR PRECEDINGなどのインターバル指定またはインターバルを求める式によって指定できます。

論理オフセットでは、NUMERIC(オフセットが数値の場合)またはDATE(インターバルが指定される場合)と互換性のある型の式を1つのみ、関数のORDER BY式リストに指定できます。

RANGEキーワードを使用する分析関数で、ウィンドウとして次の2つのいずれかを指定する場合は、ORDER BY句に複数のソート・キーを指定できます。

- RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。このウィンドウの短縮形はRANGE UNBOUNDED PRECEDINGです。この短縮形を使用することもできます。

- RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING。

ウィンドウの境界がこれらの条件に合致しない場合、分析関数のORDER BY句に指定できるソート・キーは1つのみとなります。

例22-7 累積集計関数

2000年の四半期別、顧客ID別の累積amount_soldの例を次に示します。

SELECT c.cust_id, t.calendar_quarter_desc, TO_CHAR (SUM(amount_sold),
'9,999,999,999.99') AS Q_SALES, TO_CHAR(SUM(SUM(amount_sold))
OVER (PARTITION BY c.cust_id ORDER BY c.cust_id, t.calendar_quarter_desc
ROWS UNBOUNDED
PRECEDING), '9,999,999,999.99') AS CUM_SALES
FROM sales s, times t, customers c
WHERE s.time_id=t.time_id AND s.cust_id=c.cust_id AND t.calendar_year=2000
AND c.cust_id IN (2595, 9646, 11111)
GROUP BY c.cust_id, t.calendar_quarter_desc
ORDER BY c.cust_id, t.calendar_quarter_desc;

CUST_ID CALENDA Q_SALES           CUM_SALES
---------- ------- ----------------- -----------------
2595 2000-01            659.92            659.92
2595 2000-02            224.79            884.71
2595 2000-03            313.90          1,198.61
2595 2000-04          6,015.08          7,213.69
9646 2000-01          1,337.09          1,337.09
9646 2000-02            185.67          1,522.76
9646 2000-03            203.86          1,726.62
9646 2000-04            458.29          2,184.91
11111 2000-01             43.18             43.18
11111 2000-02             33.33             76.51
11111 2000-03            579.73            656.24
11111 2000-04            307.58            963.82

この例では、分析関数SUMによって各行のウィンドウが定義されます。ウィンドウはパーティションの先頭から開始され(UNBOUNDED PRECEDING)、デフォルトではカレント行で終了します。

この例では、それ自体がSUMである値に対してSUMを実行しているため、ネストされたSUMが必要です。ネストされた集計は、分析集計関数できわめて頻繁に使用されます。

例22-8 移動集計関数

次に、時間ベースのウィンドウの例を示します。このウィンドウは、特定の顧客について、今月と過去2か月間の売上の移動平均を示すものです。

SELECT c.cust_id, t.calendar_month_desc, TO_CHAR (SUM(amount_sold),
'9,999,999,999') AS SALES, TO_CHAR(AVG(SUM(amount_sold))
OVER (ORDER BY c.cust_id, t.calendar_month_desc ROWS 2 PRECEDING),
'9,999,999,999') AS MOVING_3_MONTH_AVG
FROM sales s, times t, customers c
WHERE s.time_id=t.time_id AND s.cust_id=c.cust_id
AND t.calendar_year=1999 AND c.cust_id IN (6510)
GROUP BY c.cust_id, t.calendar_month_desc
ORDER BY c.cust_id, t.calendar_month_desc;

CUST_ID CALENDAR SALES          MOVING_3_MONTH
---------- -------- -------------- --------------
6510 1999-04             125            125
6510 1999-05           3,395          1,760
6510 1999-06           4,080          2,533
6510 1999-07           6,435          4,637
6510 1999-08           5,105          5,207
6510 1999-09           4,676          5,405
6510 1999-10           5,109          4,963
6510 1999-11             802          3,529

ウィンドウ計算は問合せにより取り出されたデータの境界を越えることはないため、出力データの3か月の移動平均計算のうち、最初の2行は指定したよりも小さいインターバルに基づいていることに注意してください。結果セットの境界で見られるこのような異なるウィンドウ・サイズを考慮する必要があります。つまり、必要な内容のみが含まれるように問合せの変更が必要になることがあります。

集中集計関数

カレント行の前後に集中する集計ウィンドウ関数の計算は簡単です。次の例では、すべての顧客について、1999年12月末の1週間における売上の集中移動平均を計算します。ここで、カレント行の前後1日(カレント行も含む)の合計売上の平均がわかります。

例22-9 集中集計

SELECT t.time_id, TO_CHAR (SUM(amount_sold), '9,999,999,999')
AS SALES, TO_CHAR(AVG(SUM(amount_sold)) OVER
(ORDER BY t.time_id
RANGE BETWEEN INTERVAL '1' DAY PRECEDING AND
INTERVAL '1' DAY FOLLOWING), '9,999,999,999') AS CENTERED_3_DAY_AVG
FROM sales s, times t
WHERE s.time_id=t.time_id AND t.calendar_week_number IN (51)
AND calendar_year=1999
GROUP BY t.time_id
ORDER BY t.time_id;

TIME_ID   SALES          CENTERED_3_DAY
--------- -------------- --------------
20-DEC-99        134,337        106,676
21-DEC-99         79,015        102,539
22-DEC-99         94,264         85,342
23-DEC-99         82,746         93,322
24-DEC-99        102,957         82,937
25-DEC-99         63,107         87,062
26-DEC-99         95,123         79,115

ウィンドウ計算は問合せにより取り出されたデータの境界は越えないため、出力データの各製品の集中移動平均計算のうち、最初の行と最後の行は2日にのみ基づいています。ユーザーは、結果セットの境界で見られるこのような異なるウィンドウ・サイズを考慮する必要があります。場合によっては、問合せの調整が必要となります。

重複がある場合の集計ウィンドウ関数

次の例では、同じ内容の値のデータが存在する場合、つまり単一の順序値に対して複数の行が戻される場合に、集計ウィンドウ関数で値がどのように計算されるかを示します。次の問合せでは、特定の期間における複数の顧客に対する売上高が取得されます。(実データセットを定義するためにインライン・ビューを使用していますが、特に意味はないので無視してかまいません。)この問合せには、カレント行の日付から10日前まで実行される変動ウィンドウが定義されています。この例のウィンドウ句の定義にRANGEキーワードが使用されていることに注意してください。これは、このウィンドウが、範囲内にある各値に対して多くの行を保持できることを意味しています。この場合、重複するデータ値を持つ行が3組あります。

例22-10 論理オフセットを指定した集計ウィンドウ関数

SELECT time_id, daily_sum, SUM(daily_sum) OVER (ORDER BY time_id
RANGE BETWEEN INTERVAL '10' DAY PRECEDING AND CURRENT ROW)
AS current_group_sum
FROM (SELECT time_id, channel_id, SUM(s.quantity_sold)
AS daily_sum
FROM customers c, sales s, countries
WHERE c.cust_id=s.cust_id
AND c.country_id = countries.country_id
AND s.cust_id IN (638, 634, 753, 440 ) AND s.time_id BETWEEN '01-MAY-00'
AND '13-MAY-00' GROUP BY time_id, channel_id);

TIME_ID    DAILY_SUM CURRENT_GROUP_SUM
--------- ---------- -----------------
06-MAY-00          7                 7   /* 7 */
10-MAY-00          1                 9   /* 7 + (1+1) */
10-MAY-00          1                 9   /* 7 + (1+1) */
11-MAY-00          2                15   /* 7 + (1+1) + (2+4) */
11-MAY-00          4                15   /* 7 + (1+1) + (2+4) */
12-MAY-00          1                16   /* 7 + (1+1) + (2+4) + 1 */
13-MAY-00          2                23   /* 7 + (1+1) + (2+4) + 1 + (5+2) */
13-MAY-00          5                23   /* 7 + (1+1) + (2+4) + 1 + (5+2) */

この例の出力では、5月6日と5月12日を除くすべての日付について2つの行が戻されています。出力の右側のコメントにある数字をチェックして、値がどのように計算されているかを確認します。カッコ内の各グループは、単一の日について戻された値を表します。

この例は、ROWSキーワードではなくRANGEキーワードを使用した場合にのみ当てはまることに注意してください。RANGEを使用すると、分析関数のORDER BY句でORDER BY式を1つしか使用できないことにも注意が必要です。ROWSキーワードを使用した場合は、分析関数のORDER BY句で複数のORDER BY式を使用できます。

行ごとに変動するウィンドウ・サイズ

特定の条件に応じて、行ごとにウィンドウのサイズを変えると便利な場合があります。特定の日付に対してウィンドウを大きくし、その他の日付には小さくする必要がある場合などです。たとえば、3営業日にわたる株価の移動平均を計算するとします。すべての営業日で毎日の行数が同じで、非営業日については格納されていない場合は、物理ウィンドウ関数を使用できます。この条件が満たされない場合に移動平均を計算するには、ウィンドウ・サイズ・パラメータで式を使用します。

ウィンドウ・サイズ指定における式は、様々なソースで作成できます。式には、時間表などの表の列を使用できます。また、カレント行の値に基づいてウィンドウの適切な境界を戻す関数も使用できます。仮想の株価データベースに関する次の文では、RANGE句にユーザー定義関数を使用してウィンドウ・サイズが設定されています。

SELECT t_timekey, AVG(stock_price)
OVER (ORDER BY t_timekey RANGE fn(t_timekey) PRECEDING) av_price
FROM stock, time WHERE st_timekey = t_timekey
ORDER BY t_timekey;

この文では、t_timekeyが日付フィールドです。fnは、次の仕様を持つPL/SQLファンクションとします。

fn(t_timekey)は次の値を戻します。

- t_timekeyが月曜日または火曜日の場合は4。

- それ以外の場合は2。

- 前に休日があっても、カウントは正しく調整されます。

日付列に関して、ウィンドウ関数でORDER BY句に数値が使用される形でウィンドウが指定されている場合、この数値は日数に変換されます。単にfn(t_timekey)とするのではなく、インターバル・リテラル変換関数をNUMTODSINTERVAL(fn(t_timekey), 'DAY')のように使用しても、同じ処理を行えます。また、INTERVALデータ型の値を戻すPL/SQLファンクションを記述することもできます。

物理オフセットを指定した集計ウィンドウ関数の例

行単位で表されるウィンドウでは、順序付け式は、結果を確定的にするために一意である必要があります。たとえば、次の問合せは、この結果セット内でtime_idが一意でないため、確定的ではありません。

例22-11 物理オフセットを指定した集計ウィンドウ関数

SELECT t.time_id, TO_CHAR(amount_sold, '9,999,999,999') AS INDIV_SALE,
TO_CHAR(SUM(amount_sold) OVER (PARTITION BY t.time_id ORDER BY t.time_id
ROWS UNBOUNDED PRECEDING), '9,999,999,999') AS CUM_SALES
FROM sales s, times t, customers c
WHERE s.time_id=t.time_id AND s.cust_id=c.cust_id
AND t.time_id IN
(TO_DATE('11-DEC-1999'), TO_DATE('12-DEC-1999'))
AND c.cust_id
BETWEEN 6500 AND 6600
ORDER BY t.time_id;

TIME_ID   INDIV_SALE    CUM_SALES
--------- ----------    ---------
12-DEC-99         23           23
12-DEC-99          9           32
12-DEC-99         14           46
12-DEC-99         24           70
12-DEC-99         19           89

この問題に対処するには、結果セットにprod_id列を追加してtime_idおよびprod_idの両方を順序付けするという方法があります。

レポート

問合せの処理後に、結果の行数や列の平均値といった集計値をパーティション内で簡単に計算でき、他のレポート関数でも使用可能にできます。集計レポート関数では、パーティション内のすべての行に対して同一の集計値が戻されます。この種の関数におけるNULLへの対応は、SQL集計関数と同じです。構文は次のとおりです。

{SUM | AVG | MAX | MIN | COUNT | STDDEV | VARIANCE  ... }
([ALL | DISTINCT] {value expression1 [,...] | *})
OVER ([PARTITION BY value expression2[,...]])

さらに、次の条件が適用されます。

- アスタリスク(*)は、COUNT(*)でのみ使用できます。

- DISTINCTは、対応する集計関数で許可されている場合にのみサポートされます。

- value expression1およびvalue expression2には、列参照または集計を含む有効な式を指定できます。

- PARTITION BY句には、ウィンドウ関数の計算対象とするグループを定義します。PARTITION BY句がない場合、この関数は問合せ結果セット全体に対して計算されます。

レポート関数は、SELECT句またはORDER BY句でのみ使用できます。レポート関数の主なメリットは、単一の問合せブロックでデータの複数のパスを実行し、問合せパフォーマンスをスピードアップできることです。「売上が市全体の売上の10%以上である販売員の数をカウントする」といった問合せで、別々の問合せブロック間の結合が必要ありません。

たとえば、「各製品カテゴリについて、最大の売上を記録した地域を検索する」という質問について考えてみます。MAX集計レポート関数を使用する同等のSQL問合せは、次のようになります。

SELECT prod_category, country_region, sales
FROM (SELECT SUBSTR(p.prod_category,1,8) AS prod_category, co.country_region,
SUM(amount_sold) AS sales,
MAX(SUM(amount_sold)) OVER (PARTITION BY prod_category) AS MAX_REG_SALES
FROM sales s, customers c, countries co, products p
WHERE s.cust_id=c.cust_id AND c.country_id=co.country_id
AND s.prod_id =p.prod_id AND s.time_id = TO_DATE('11-OCT-2001')
GROUP BY prod_category, country_region)
WHERE sales = MAX_REG_SALES;

集計レポート関数MAX(SUM(amount_sold))を含む内部問合せでは、次の内容が戻されます。

PROD_CAT COUNTRY_REGION            SALES MAX_REG_SALES
-------- -------------------- ---------- -------------
Electron Americas                 581.92        581.92
Hardware Americas                 925.93        925.93
Peripher Americas                3084.48       4290.38
Peripher Asia                    2616.51       4290.38
Peripher Europe                  4290.38       4290.38
Peripher Oceania                  940.43       4290.38
Software Americas                 4445.7        4445.7
Software Asia                    1408.19        4445.7
Software Europe                  3288.83        4445.7
Software Oceania                  890.25        4445.7

問合せ結果の全体は、次のようになります。

PROD_CAT COUNTRY_REGION            SALES
-------- -------------------- ----------
Electron Americas                 581.92
Hardware Americas                 925.93
Peripher Europe                  4290.38
Software Americas                 4445.7

例22-12 集計レポートの例

集計レポートをネストされた問合せと組み合せると、複雑な問合せに対する応答が効率化されます。たとえば、最も重要な製品サブカテゴリで最も売れ行きがよい製品を知りたいとします。製品カテゴリ内の売上の20%以上を占める製品サブカテゴリについて、サブカテゴリごとに売上上位5位までの製品を検索する問合せは、次のようになります。

SELECT SUBSTR(prod_category,1,8) AS CATEG, prod_subcategory, prod_id, SALES
FROM (SELECT p.prod_category, p.prod_subcategory, p.prod_id,
SUM(amount_sold) AS SALES,
SUM(SUM(amount_sold)) OVER (PARTITION BY p.prod_category) AS CAT_SALES,
SUM(SUM(amount_sold)) OVER
(PARTITION BY p.prod_subcategory) AS SUBCAT_SALES,
RANK() OVER  (PARTITION BY p.prod_subcategory
ORDER BY SUM(amount_sold) ) AS RANK_IN_LINE
FROM sales s, customers c, countries co, products p
WHERE s.cust_id=c.cust_id
AND c.country_id=co.country_id AND  s.prod_id=p.prod_id
AND s.time_id=to_DATE('11-OCT-2000')
GROUP BY p.prod_category, p.prod_subcategory, p.prod_id
ORDER BY prod_category, prod_subcategory)
WHERE SUBCAT_SALES>0.2*CAT_SALES AND RANK_IN_LINE<=5;

RATIO_TO_REPORT関数

RATIO_TO_REPORT関数では、値の集合の合計に対して、ある値の割合が計算されます。式value expressionがNULLと評価される場合、RATIO_TO_REPORTもNULLと評価されますが、分母の値の合計を計算する際には0(ゼロ)として扱われます。構文は次のとおりです。

RATIO_TO_REPORT ( expr ) OVER ( [query_partition_clause] )

ここでは、次の事項が適用されます。

- exprには、列参照または集計を含む任意の有効な式を指定できます。

- PARTITION BY句には、RATIO_TO_REPORT関数の計算対象とするグループを定義します。PARTITION BY句がない場合、この関数は問合せ結果セット全体に対して計算されます。

例22-13 RATIO_TO_REPORT

チャネル別売上のRATIO_TO_REPORTを計算するには、次のような構文を使用できます。

SELECT ch.channel_desc, TO_CHAR(SUM(amount_sold),'9,999,999') AS SALES,
TO_CHAR(SUM(SUM(amount_sold)) OVER (), '9,999,999') AS TOTAL_SALES,
TO_CHAR(RATIO_TO_REPORT(SUM(amount_sold)) OVER (), '9.999')
AS RATIO_TO_REPORT
FROM sales s, channels ch
WHERE s.channel_id=ch.channel_id  AND s.time_id=to_DATE('11-OCT-2000')
GROUP BY ch.channel_desc;

CHANNEL_DESC         SALES      TOTAL_SALE RATIO_
-------------------- ---------- ---------- ------
Direct Sales             14,447     23,183   .623
Internet                    345     23,183   .015
Partners                  8,391     23,183   .362

LAG/LEAD

LAGおよびLEAD関数は、行の相対位置を確実に認識できる場合の、値の比較に役立ちます。この2つの関数は、カレント行からターゲット行までの行数を指定する形で使用します。これらの関数では、表の複数の行に対して自己結合せずに同時にアクセスできるため、処理速度が向上します。LAG関数では、現在位置から指定したオフセット分のみ前の行がアクセスされ、LEAD関数では、現在位置から指定したオフセット分のみ後の行がアクセスされます。

NTH_VALUE関数と関連があり、これを単純化したものであると考えられるのがLAG関数とLEAD関数です。LAGとLEADでは、指定した物理オフセットの行の値を取得できるだけです。これでは不十分な場合は、NTH_VALUEを使用して、いわゆる論理オフセットまたは相対位置に基づく行の値を取得できます。NTH_VALUE関数ではIGNORE NULLSオプションを指定できます。つまり、条件を指定したり、特定の条件に基づき行を除外できるという意味で、その機能を強化できます。例22-17「NTH_VALUE」では、数量が8未満の行はフィルタにより除外されています。これはLAGやLEADではできません。目的の行までのオフセットがわからないためです。

詳細は、「NTH_VALUE関数」および『Oracle Database SQL言語リファレンス』を参照してください。

LAGおよびLEAD関数の構文

これらの関数の構文は次のとおりです。

{LAG | LEAD} ( value_expr [, offset] [, default] ) [RESPECT NULLS|IGNORE NULLS]
OVER ( [query_partition_clause] order_by_clause )

offsetはオプションのパラメータで、デフォルトは1です。defaultはオプションのパラメータで、offsetが表またはパーティションの境界外となる場合に戻される値です。IGNORE NULLSを指定した場合、戻される値は、NULLの行を無視した上で、指定されたLAGまたはLEADのオフセットの行から取得されます。

例22-14 LAG/LEAD

この例は、LAGおよびLEADの一般的な使用例を示しています。

SELECT time_id, TO_CHAR(SUM(amount_sold),'9,999,999') AS SALES,
TO_CHAR(LAG(SUM(amount_sold),1) OVER (ORDER BY time_id),'9,999,999') AS LAG1,
TO_CHAR(LEAD(SUM(amount_sold),1) OVER (ORDER BY time_id),'9,999,999') AS LEAD1
FROM sales
WHERE time_id>=TO_DATE('10-OCT-2000') AND time_id<=TO_DATE('14-OCT-2000')
GROUP BY time_id;

TIME_ID   SALES      LAG1       LEAD1
--------- ---------- ---------- ----------
10-OCT-00    238,479                23,183
11-OCT-00     23,183    238,479     24,616
12-OCT-00     24,616     23,183     76,516
13-OCT-00     76,516     24,616     29,795
14-OCT-00     29,795     76,516

LAG/LEAD関数を使用して、スパースなデータに対する周期ごとの比較問合せを実行する方法は、「レポート用のデータの稠密化」を参照してください。

例22-15 IGNORE NULLSを使用したLAG/LEAD

この例は、LAGおよびLEADでIGNORE NULLSオプションを指定する一般的な使用例を示しています。

SELECT prod_id, channel_id, SUM(quantity_sold) quantity,
CASE WHEN SUM(quantity_sold) < 5000 THEN SUM(amount_sold) ELSE NULL END amount,
LAG(CASE WHEN SUM(quantity_sold) < 5000 THEN SUM(amount_sold) ELSE NULL END)
IGNORE NULLS OVER (PARTITION BY prod_id ORDER BY channel_id) lag
FROM sales
WHERE prod_id IN (18,127,138)
GROUP BY prod_id, channel_id;

PROD_ID  CHANNEL_ID  QUANTITY   AMOUNT          LAG
--------  ----------  --------   -----------     ----------
18           2      2888    4420923.94
18           3      5615                   4420923.94
18           4      1088    1545729.81     4420923.94
127           2      4508     274088.08
127           3      9626                    274088.08
127           4      1850     173682.67      274088.08
138           2      1120      127390.3
138           3      3878     393111.15       127390.3
138           4       543      71203.21      393111.15

9 rows selected.

FIRST_VALUE関数、LAST_VALUE関数およびNTH_VALUE関数

この項では、FIRST_VALUE関数、LAST_VALUE関数、およびNTH_VALUE関数について説明します。

FIRST_VALUE関数およびLAST_VALUE関数

FIRST_VALUE関数やLAST_VALUE関数を使用すると、ウィンドウの最初や最後の行を選択できます。これらの行は、計算上の基準行として使用されるので特に重要です。これらの行を使用する例としては、たとえば、日付で順序付けされた売上データが保持されるパーティションについて、「その期間の最初の販売日(FIRST_VALUE)と比較した各日の売上はどの程度か」といった質問をする場合があります。

FIRST_VALUEでIGNORE NULLSオプションを使用すると、セット内で最初のNULL以外の値が戻されます。すべての値がNULLの場合は、NULLが戻されます。LAST_VALUEでIGNORE NULLSを使用すると、セット内で最後のNULL以外の値が戻されます。すべての値がNULLの場合は、NULLが戻されます。IGNORE NULLSオプションは、在庫表を適切に移入する場合に特に役立ちます。

これらの関数の構文は次のとおりです。

FIRST_VALUE|LAST_VALUE ( <expr> ) [RESPECT NULLS|IGNORE NULLS] OVER (analytic clause );

例22-16 FIRST_VALUE

この例では、FIRST_VALUEでIGNORE NULLSオプションを使用しています。

SELECT prod_id, channel_id, time_id,
CASE WHEN MIN(amount_sold) > 9.5
THEN MIN(amount_sold) ELSE NULL END amount_sold,
FIRST_VALUE(CASE WHEN MIN(amount_sold) > 9.5
THEN min(amount_sold) ELSE NULL END)
IGNORE NULLS OVER (PARTITION BY prod_id
ORDER BY channel_id DESC, time_id
ROWS BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING) nv FROM sales
WHERE prod_id = 115 AND time_id BETWEEN '18-DEC-01'
AND '22-DEC-01' GROUP BY prod_id, channel_id, time_id
ORDER BY prod_id;

PROD_ID CHANNEL_ID TIME_ID    AMOUNT_SOLD         NV
---------- ---------- --------- ----------- ----------
115          4 18-DEC-01                   9.66
115          4 19-DEC-01                   9.66
115          4 20-DEC-01                   9.66
115          4 22-DEC-01                   9.66
115          3 18-DEC-01        9.66       9.66
115          3 19-DEC-01        9.66       9.66
115          3 20-DEC-01        9.66       9.66
115          3 21-DEC-01        9.66       9.66
115          3 22-DEC-01        9.66       9.66
115          2 18-DEC-01        9.67       9.66
115          2 19-DEC-01        9.67       9.66
115          2 21-DEC-01        9.67       9.66
115          2 22-DEC-01        9.67       9.66

13 rows selected.

NTH_VALUE関数

NTH_VALUE関数では、ウィンドウ内の任意の行から列値を見つけることができます。たとえば、ある企業の株価について、ある年で5番目に高かった終値を検索する場合に使用できます。

NTH_VALUE関数と関連があり、これを単純化したものであると考えられるのがLAG関数とLEAD関数です。LAGとLEADでは、指定した物理オフセットの行の値を取得できるだけです。これでは不十分な場合は、NTH_VALUEを使用して、いわゆる論理オフセットまたは相対位置に基づく行の値を取得できます。NTH_VALUE、FIRST_VALUEおよびLAST_VALUE関数では、IGNORE NULLSオプションを使用して、条件を指定したり、特定の条件に基づいて行を除外できるという意味で、その機能を強化できます。例22-17「NTH_VALUE」では、数量が8未満の行はフィルタにより除外されています。これはLAGやLEADではできません。目的の行までのオフセットがわからないためです。

詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

この関数の構文は次のとおりです。

NTH_VALUE (<expr>, <n expr>) [FROM FIRST | FROM LAST]
[RESPECT NULLS | IGNORE NULLS] OVER (<window specification>)

- exprには、列、定数、バインド変数、またはこれらを含む式を指定できます。

- nには、列、定数、バインド変数、またはこれらを含む式を指定できます。

- RESPECT NULLSは、NULLを処理するデフォルトのメカニズムです。exprのNULL値を計算に含めるか、計算から除外するかを決定します。デフォルトはRESPECT NULLSです。

- FROM FIRSTおよびFROM LASTオプションでは、オフセットnが最初の行からか最後の行からかを指定できます。デフォルトはFROM FIRSTです。

- IGNORE NULLSを指定すると、メジャー値内のNULLをスキップできます。

例22-17 NTH_VALUE

次の例は、prod_idが10から20の範囲の各製品について、昇順で2番目のchannel_idのamount_sold値を戻します。

SELECT prod_id, channel_id, MIN(amount_sold),
NTH_VALUE(MIN(amount_sold), 2) OVER (PARTITION BY prod_id ORDER BY channel_id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) NV
FROM sales
WHERE prod_id BETWEEN 10 AND 20 GROUP BY prod_id, channel_id;

PROD_ID CHANNEL_ID MIN(AMOUNT_SOLD)         NV
---------- ---------- ---------------- ----------
13          2           907.34      906.2
13          3            906.2      906.2
13          4           842.21      906.2
14          2          1015.94    1036.72
14          3          1036.72    1036.72
14          4           935.79    1036.72
15          2           871.19     871.19
15          3           871.19     871.19
15          4           871.19     871.19
16          2           266.84     266.84
16          3           266.84     266.84
16          4           266.84     266.84
16          9            11.99     266.84
...

分析用の高度な集計

この項では、次に示す高度な分析集計関数について説明します。

- LISTAGG関数

- FIRST/LAST関数

- 逆パーセンタイル

- 仮説ランク

- 線形回帰

- 統計集計

- ユーザー定義集計

LISTAGG関数

LISTAGG関数は、ORDER BY句に基づいて各グループ内のデータを順序付けてから、メジャー列の値を連結します。構文は次のとおりです。

LISTAGG (<expr> [, <delimiter>) WITHIN GROUP (ORDER BY <oby_expression_list>)

exprには、列、定数、バインド変数、またはこれらを含む式を指定できます。

delimiterには、列、定数、バインド変数、またはこれらを含む式を指定できます。

oby_expression_listには、任意の順序付けオプションを含む式のリストを指定できます。昇順または降順でソートしたり(ASCまたはDESC)、NULLのソート順を制御したり(NULLS FIRSTまたはNULLS LAST)するときに使用します。デフォルトは、ASCENDINGおよびNULLS LASTです。

集計として使用するLISTAGG

LISTAGG関数を集計として使用できます。

例22-18 集計として使用するLISTAGG

次の例では、LISTAGGを集計として使用しています。

SELECT prod_id, LISTAGG(cust_first_name||' '||cust_last_name, '; ')
WITHIN GROUP (ORDER BY amount_sold DESC) cust_list
FROM sales, customers
WHERE sales.cust_id = customers.cust_id AND cust_gender = 'M'
AND cust_credit_limit = 15000 AND prod_id BETWEEN 15 AND 18
AND channel_id = 2 AND time_id > '01-JAN-01'
GROUP BY prod_id;

PROD_ID  CUST_LIST
-------  -----------------------------------------------
15  Hope Haarper; Roxanne Crocker; ... Mason Murray
16  Manvil Austin; Bud Pinkston; ... Helga Nickols
17  Opal Aaron; Thacher Rudder; ... Roxanne Crocker
18  Boyd Lin; Bud Pinkston; ... Erik Ready

この出力は、読みやすく変更されています。

集計レポートとして使用するLISTAGG

LISTAGG関数を集計レポートとして使用できます。

例22-19 集計レポートとして使用するLISTAGG

この例では、LISTAGGを集計レポートとして使用しています。

connect sh/sh
set lines 120 pages 20000
column list format A40

SELECT time_id, prod_id, MIN(amount_sold), LISTAGG(min(amount_sold),';')
WITHIN GROUP (ORDER BY prod_id) OVER (PARTITION BY time_id) cust_list
FROM sales WHERE time_id > '20-DEC-01' AND prod_id BETWEEN 120 AND 125
GROUP BY prod_id, time_id;

TIME_ID   PROD_ID   MIN(AMOUNT_SOLD)  CUST_LIST
-------   -------   ----------------  -----------
21-DEC-01     120            51.36    51.36;10.81
21-DEC-01     121            10.81    51.36;10.81
22-DEC-01     120            51.36    51.36;10.81;20.23;56.12;17.79;15.67
22-DEC-01     121            10.81    51.36;10.81;20.23;56.12;17.79;15.67
22-DEC-01     122            20.23    51.36;10.81;20.23;56.12;17.79;15.67
22-DEC-01     123            56.12    51.36;10.81;20.23;56.12;17.79;15.67
22-DEC-01     124            17.79    51.36;10.81;20.23;56.12;17.79;15.67
22-DEC-01     125            15.67    51.36;10.81;20.23;56.12;17.79;15.67
...

FIRST/LAST関数

FIRST/LAST集計関数を使用すると、データセットをランク付けした上で、最上位または最下位の行に対する処理ができます。最上位または最下位の行が発見された後には、目的とする任意の列に対して集計関数が適用されます。つまり、FIRST/LASTでは、列Aを対象としてランク付けした結果、最上位または最下位となった行の、列Bにおける値に対する集計の結果が戻されます。この場合、自己結合や副問合せが不要になり、より高いパフォーマンスが得られるので便利です。FIRST関数およびLAST関数の構文としては、各グループに対し1つの戻り値を生成する通常の集計関数(MIN、MAX、SUM、AVG、COUNT、VARIANCE、STDDEV)を最初に記述します。その上で、FIRST/LAST関数で使用するランキングを指定するため、KEEPで始まる新しい句を追加します。

これらの関数の構文は次のとおりです。

aggregate_function KEEP ( DENSE_RANK LAST ORDER BY
expr [ DESC | ASC ] [NULLS { FIRST | LAST }]
[, expr [ DESC | ASC ] [NULLS { FIRST | LAST }]]...)
[OVER query_partitioning_clause]

ORDER BY句には、複数の式を指定できます。

通常の集計としてのFIRSTおよびLAST

FIRST/LAST集計ファミリは、通常の集計関数として使用できます。

例22-20 FIRSTおよびLASTの例1

次に示す問合せでは、製品の最低価格と定価を比較できます。メンズ・ウェア・カテゴリにおける製品サブカテゴリごとに、次の内容が戻されます。

- 最低価格が最も低い製品の定価

- 最も低い最低価格

- 最低価格が最も高い製品の定価

- 最も高い最低価格

SELECT prod_subcategory, MIN(prod_list_price)
KEEP (DENSE_RANK FIRST ORDER BY (prod_min_price)) AS LP_OF_LO_MINP,
MIN(prod_min_price) AS LO_MINP,
MAX(prod_list_price) KEEP (DENSE_RANK LAST ORDER BY (prod_min_price))
AS LP_OF_HI_MINP,
MAX(prod_min_price) AS HI_MINP
FROM products WHERE prod_category='Electronics'
GROUP BY prod_subcategory;

PROD_SUBCATEGORY LP_OF_LO_MINP LO_MINP  LP_OF_HI_MINP  HI_MINP
---------------- ------------- -------  ------------- ----------
Game Consoles          299.99  299.99          299.99     299.99
Home Audio             499.99  499.99          599.99     599.99
Y Box Accessories        7.99    7.99           20.99      20.99
Y Box Games              7.99    7.99           29.99      29.99

集計レポートとしてのFIRSTおよびLAST

FIRST/LAST集計ファミリは、集計レポート関数としても使用できます。たとえば、人数の増加が年間で最大の月や最小の月の計算などができます。これらの関数の構文は、他の集計レポートの構文と似ています。

例22-20のFIRST/LASTの例で考えてみます。この場合、「個々の製品の定価を検索し、それをサブカテゴリ内で最低価格が最も高い製品および最も低い製品の定価と比較するとどうなるか」という問題になります。

FIRST/LASTを集計レポートとして使用し、こうした情報をドキュメンテーション(Documentation)というサブカテゴリに関して検索する問合せを次に示します。

例22-21 FIRSTおよびLASTの例2

SELECT prod_id, prod_list_price,
MIN(prod_list_price) KEEP (DENSE_RANK FIRST ORDER BY (prod_min_price))
OVER(PARTITION BY (prod_subcategory)) AS LP_OF_LO_MINP,
MAX(prod_list_price) KEEP (DENSE_RANK LAST ORDER BY (prod_min_price))
OVER(PARTITION BY (prod_subcategory)) AS LP_OF_HI_MINP
FROM products WHERE prod_subcategory = 'Documentation';

PROD_ID PROD_LIST_PRICE LP_OF_LO_MINP LP_OF_HI_MINP
---------- --------------- ------------- -------------
40           44.99         44.99         44.99
41           44.99         44.99         44.99
42           44.99         44.99         44.99
43           44.99         44.99         44.99
44           44.99         44.99         44.99
45           44.99         44.99         44.99

FIRSTおよびLAST関数を集計レポートとして使用すると、その結果を簡単に「最高給与に対する給与の割合」などの計算に簡単に組み込むことができます。

逆パーセンタイル

値の集合の累積分布(パーセンタイル)を求めるには、CUME_DIST関数を使用できます。ただし、逆の操作(特定のパーセンタイルを計算するための値の検索)は、簡単でも効率的でもありません。これを簡単に操作できるように、PERCENTILE_CONT関数とPERCENTILE_DISC関数が導入されました。この2つの関数は、通常の集計関数としてのみでなく、ウィンドウおよびレポート関数としても使用できます。

これらの関数では、ソートの指定と、0から1のパーセンタイル値をとるパラメータが必要とされます。ソート指定の処理には、ORDER BY句で1つの式を使用します。通常の集計関数として使用すると、ソートされた集合ごとに単一の値が戻されます。

PERCENTILE_CONTは内挿法により計算される連続関数で、PERCENTILE_DISCは不連続値を想定するステップ関数です。PERCENTILE_CONTおよびPERCENTILE_DISCは、他の集計と同様、グルーピングされた問合せの行グループを対象として操作する関数ですが、次のような違いがあります。

- 0から1までの値をとるパラメータが必要です。パラメータにこの範囲外の値を指定した場合、エラーとなります。このパラメータには、定数に評価される式を指定する必要があります。

- ソート指定が必要です。ソートは、ORDER BY句に単一の式を指定する形で指定します。複数の式は使用できません。

通常の集計の構文

[PERCENTILE_CONT | PERCENTILE_DISC]( constant expression )
WITHIN GROUP ( ORDER BY single order by expression
[ASC|DESC] [NULLS FIRST| NULLS LAST])

逆パーセンタイルの例

次の問合せを使用して、この項の例で使用したデータのうち17行を戻すとします。

SELECT cust_id, cust_credit_limit, CUME_DIST()
OVER (ORDER BY cust_credit_limit) AS CUME_DIST
FROM customers WHERE cust_city='Marshal';

CUST_ID CUST_CREDIT_LIMIT  CUME_DIST
---------- ----------------- ----------
28344              1500 .173913043
8962              1500 .173913043
36651              1500 .173913043
32497              1500 .173913043
15192              3000 .347826087
102077              3000 .347826087
102343              3000 .347826087
8270              3000 .347826087
21380              5000  .52173913
13808              5000  .52173913
101784              5000  .52173913
30420              5000  .52173913
10346              7000 .652173913
31112              7000 .652173913
35266              7000 .652173913
3424              9000 .739130435
100977              9000 .739130435
103066             10000 .782608696
35225             11000 .956521739
14459             11000 .956521739
17268             11000 .956521739
100421             11000 .956521739
41496             15000          1

PERCENTILE_DISC(x)の計算では、x以上の値が最初に見つかるまで、各グループ内のCUME_DIST値がスキャンされます。xは、指定した百分位数です。このサンプルの問合せではPERCENTILE_DISC(0.5)となり、結果は次のように5,000となります。

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP
(ORDER BY cust_credit_limit) AS perc_disc, PERCENTILE_CONT(0.5) WITHIN GROUP
(ORDER BY cust_credit_limit) AS perc_cont
FROM customers WHERE cust_city='Marshal';

PERC_DISC   PERC_CONT
---------   ---------
5000        5000

PERCENTILE_CONTの計算では、順序付けを行った後に、行と行の線形内挿法によって結果が導かれます。PERCENTILE_CONT(x)を計算するため、まず行番号=RN= (1+x* (n-1))が計算されます。nはグループ内の行数、xは指定した百分位数です。さらに、行番号CRN = CEIL(RN)およびFRN = FLOOR(RN)の行の値の線形内挿法により、この集計関数の最終結果が計算されます。

最終結果は、PERCENTILE_CONT(X) =(CRN = FRN = RN)の場合は(行RNの式の値)、それ以外の場合は(CRN - RN)*(行FRNの式の値)+(RN -FRN)*(行CRNの式の値)となります。

前述の問合せの例で、PERCENTILE_CONT(0.5)を計算する場合を考えてみます。ここで、nは17です。どちらのグループも、行番号はRN = (1 + 0.5*(n-1))= 9となります。これを式に当てはめると(FRN=CRN=9)となり、結果として行9からの値が戻されます。

別の例として、PERCENTILE_CONT(0.66)を計算する場合を考えてみます。行番号は、RN=(1 + 0.66*(n-1))= (1 + 0.66*16)= 11.67と計算されます。その結果、PERCENTILE_CONT(0.66) = (12-11.67)*(行11の値)+(11.67-11)*(行12の値)となります。結果は次のようになります。

SELECT PERCENTILE_DISC(0.66) WITHIN GROUP
(ORDER BY cust_credit_limit) AS perc_disc, PERCENTILE_CONT(0.66) WITHIN GROUP
(ORDER BY cust_credit_limit) AS perc_cont
FROM customers WHERE cust_city='Marshal';

PERC_DISC  PERC_CONT
---------- ----------
9000       8040

逆パーセンタイル関数は、他の既存の集計関数と同様に問合せのHAVING句に使用できます。

集計レポートとしての使用

集計関数PERCENTILE_CONTおよびPERCENTILE_DISCは、集計レポート関数としても使用できます。その場合の構文は、他の集計レポートの場合と同様です。

[PERCENTILE_CONT | PERCENTILE_DISC](constant expression)
WITHIN GROUP ( ORDER BY single order by expression
[ASC|DESC] [NULLS FIRST| NULLS LAST])
OVER ( [PARTITION BY value expression [,...]] )

次の問合せでは同じ内容(この結果セットに含まれる顧客の与信限度額の中央値)が計算されますが、後述の出力のように結果セットの各行について結果がレポートされます。

SELECT cust_id, cust_credit_limit, PERCENTILE_DISC(0.5) WITHIN GROUP
(ORDER BY cust_credit_limit) OVER () AS perc_disc,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY cust_credit_limit)
OVER () AS perc_cont
FROM customers WHERE cust_city='Marshal';

CUST_ID CUST_CREDIT_LIMIT  PERC_DISC  PERC_CONT
---------- ----------------- ---------- ----------
28344              1500       5000       5000
8962              1500       5000       5000
36651              1500       5000       5000
32497              1500       5000       5000
15192              3000       5000       5000
102077              3000       5000       5000
102343              3000       5000       5000
8270              3000       5000       5000
21380              5000       5000       5000
13808              5000       5000       5000
101784              5000       5000       5000
30420              5000       5000       5000
10346              7000       5000       5000
31112              7000       5000       5000
35266              7000       5000       5000
3424              9000       5000       5000
100977              9000       5000       5000
103066             10000       5000       5000
35225             11000       5000       5000
14459             11000       5000       5000
17268             11000       5000       5000
100421             11000       5000       5000
41496             15000       5000       5000

制限

PERCENTILE_DISCの場合は、ORDER BY句の式に、ソート可能なデータ型(数値、文字列、日付など)を使用できます。一方、PERCENTILE_CONTの評価には線形内挿法が使用されるため、ORDER BY句の式には数値型または日付/時刻型(インターバルを含む)を指定する必要があります。式がDATE型の場合、内挿の結果はその型の最小単位に丸められます。DATE型の場合、内挿された値は最も近い秒に丸められ、インターバル型の場合は最も近い秒(INTERVAL DAY TO SECOND)または月(INTERVAL YEAR TO MONTH)に丸められます。

他の集計と同様に、逆パーセンタイル関数でも結果の評価時にNULLは無視されます。たとえば、集合内の中央値を求める場合、Oracle DatabaseではNULLが無視され、NULL以外の値から中央値が求められます。ORDER BY句にはNULLS FIRST/NULLS LASTオプションも使用できますが、NULLが無視されるので、こうしたオプションも結果的には無視されます。

仮説ランク

この種の関数では、what-if分析に役立つ機能が提供されます。たとえば、行を他の行集合に仮に挿入すると、行のランクがどうなるかという問題があるとします。

この集計ファミリでは、仮に挿入する行と順序付けられた行のグループを引数として1つ以上取り、対象とするグループに行を仮に挿入した場合の、その行のRANK、DENSE_RANK、PERCENT_RANKまたはCUME_DISTが戻されます。

[RANK | DENSE_RANK | PERCENT_RANK | CUME_DIST]( constant expression [, ...] )
WITHIN GROUP ( ORDER BY order by expression [ASC|DESC] [NULLS FIRST|NULLS LAST][, ...] )

ここで、constant expressionは定数に評価される式です。このような式を引数として複数個、関数に渡す場合もあります。ORDER BY句には、ランキングの基準となるソート順を定義する式を1つ以上含めることができます。ORDER BY句内の各式には、ASC、DESC、NULLS FIRST、NULLS LASTオプションを使用できます。

例22-22 仮説ランク関数および仮説分布関数の例1

この項で使用されているproducts表の定価データを使用して、価格50ドルのセーターの仮のRANK、PERCENT_RANKおよびCUME_DISTを計算し、各セーター・サブカテゴリに当てはまるかどうかを調べる場合を考えます。この場合の問合せと結果は次のようになります。

SELECT cust_city,
RANK(6000) WITHIN GROUP (ORDER BY CUST_CREDIT_LIMIT DESC) AS HRANK,
TO_CHAR(PERCENT_RANK(6000) WITHIN GROUP
(ORDER BY cust_credit_limit),'9.999') AS HPERC_RANK,
TO_CHAR(CUME_DIST (6000) WITHIN GROUP
(ORDER BY cust_credit_limit),'9.999') AS HCUME_DIST
FROM customers
WHERE cust_city LIKE 'Fo%'
GROUP BY cust_city;

CUST_CITY                           HRANK HPERC_ HCUME_
------------------------------ ---------- ------ ------
Fondettes                              13   .455   .478
Fords Prairie                          18   .320   .346
Forest City                            47   .370   .378
Forest Heights                         38   .456   .464
Forestville                            58   .412   .418
Forrestcity                            51   .438   .444
Fort Klamath                           59   .356   .363
Fort William                           30   .500   .508
Foxborough                             52   .414   .420

逆パーセンタイル集計とは異なり、仮説ランク関数および仮説分布関数の場合、ソート指定のORDER BY句には複数の式を使用できます。ORDER BY句の式の数と引数の数は同じにする必要があり、引数は、対応するORDER BY句の式と同じ型または互換性のある型の定数式にする必要があります。次の例では、複数の仮説ランク関数において2つの引数が使用されています。

例22-23 仮説ランク関数および仮説分布関数の例2

SELECT prod_subcategory,
RANK(10,8) WITHIN GROUP (ORDER BY prod_list_price DESC,prod_min_price)
AS HRANK, TO_CHAR(PERCENT_RANK(10,8) WITHIN GROUP
(ORDER BY prod_list_price, prod_min_price),'9.999') AS HPERC_RANK,
TO_CHAR(CUME_DIST (10,8) WITHIN GROUP
(ORDER BY prod_list_price, prod_min_price),'9.999') AS HCUME_DIST
FROM products WHERE prod_subcategory LIKE 'Recordable%'
GROUP BY prod_subcategory;

PROD_SUBCATEGORY          HRANK   HPERC_   HCUME_
--------------------      -----   ------   ------
Recordable CDs                4     .571     .625
Recordable DVD Discs          5     .200     .333

これらの関数は、他の集計関数と同様に問合せのHAVING句に使用できます。集計レポート関数や集計ウィンドウ関数としては使用できません。

線形回帰

この回帰関数では、数値の組の集合に対する微分最小2乗法で求めた回帰直線の適合がサポートされます。この関数は、集計関数としても、ウィンドウ関数やレポート関数としても使用できます。

回帰関数には次のようなものがあります。

- REGR_COUNT関数

- REGR_AVGY関数およびREGR_AVGX関数

- REGR_SLOPE関数およびREGR_INTERCEPT関数

- REGR_R2関数

- REGR_SXX関数、REGR_SYY関数およびREGR_SXY関数

Oracleではこの種の関数は、(e1, e2)の組の集合に対して、e1またはe2のいずれかがNULLである組をすべて排除した後に適用されます。e1は従属変数の値(y)として、e2は独立変数(x)として解釈されます。どちらの式も、数値である必要があります。

すべての回帰関数は、データからの単一パスの間に同時に計算されます。こうした回帰関数は、多くの場合COVAR_POP、COVAR_SAMPおよびCORR関数と組み合せて使用されます。

REGR_COUNT関数

REGR_COUNTでは、回帰直線の適合に使用されるNULL以外の数値の組の数が戻されます。この関数を空の集合に適用した場合(または、e1およびe2の両方がNULLでない(e1, e2)の組が存在しない場合)、0(ゼロ)が戻ります。

REGR_AVGY関数およびREGR_AVGX関数

REGR_AVGYでは回帰直線の従属変数の平均、REGR_AVGXでは回帰直線の独立変数の平均が計算されます。REGR_AVGYでは、(e1, e2)の組のうちe1またはe2のいずれかがNULLであるものが排除され、その後で第1の引数(e1)の平均が計算されます。同様に、REGR_AVGXでは、NULLが排除された後に第2の引数(e2)の平均が計算されます。どちらの関数も、空の集合に適用した場合にはNULLが戻ります。

REGR_SLOPE関数およびREGR_INTERCEPT関数

REGR_SLOPE関数では、NULL以外の(e1, e2)の組に適合する回帰直線の傾きが計算されます。

REGR_INTERCEPT関数では、回帰直線のy切片が計算されます。傾きまたは回帰平均がNULLの場合、REGR_INTERCEPTからはNULLが戻ります。

REGR_R2関数

REGR_R2関数では、回帰直線の確定係数(通常は「Rの2乗」または「適合度」)が計算されます。

回帰直線が定義される場合(線の傾きがNULLではない場合)、REGR_R2からは0(ゼロ)から1の値が戻ります。それ以外の場合はNULLが戻ります。値が1に近づくほど、回帰直線がデータに適合します。

REGR_SXX関数、REGR_SYY関数およびREGR_SXY関数

REGR_SXX関数、REGR_SYY関数およびREGR_SXY関数は、回帰分析用の様々な診断統計情報の計算に使用されます。これらの関数では、(e1, e2)の組のうちe1またはe2のいずれかがNULLであるものが排除された後で、次の計算が実行されます。

REGR_SXX:    REGR_COUNT(e1,e2) * VAR_POP(e2)
REGR_SYY:    REGR_COUNT(e1,e2) * VAR_POP(e1)
REGR_SXY:    REGR_COUNT(e1,e2) * COVAR_POP(e1, e2)

線形回帰統計の例

表22-2「一般的な診断統計情報およびその式」に、線形回帰分析で使用される一般的な診断統計情報の一部を示します。今回のリリースの新しい関数を使用すると、これらの情報をすべて計算できます。

表22-2 一般的な診断統計情報およびその式

統計情報のタイプ

式

調整R2

1-((1 - REGR_R2)*((REGR_COUNT-1)/(REGR_COUNT-2)))

標準誤差

SQRT((REGR_SYY-(POWER(REGR_SXY,2)/REGR_SXX))/(REGR_COUNT-2))

2乗の総計

REGR_SYY

2乗の回帰合計

POWER(REGR_SXY,2) / REGR_SXX

2乗の残差合計

REGR_SYY - (POWER(REGR_SXY,2)/REGR_SXX)

傾きのt統計

REGR_SLOPE * SQRT(REGR_SXX) /(標準誤差)

y切片のt統計量

REGR_INTERCEPT / ((標準誤差)* SQRT((1/REGR_COUNT)+(POWER(REGR_AVGX,2)/REGR_SXX))

線形回帰計算の例

この例では、製品の販売数量をその製品の定価の線形関数として表す、微分最小2乗法で求めた回帰直線を計算します。計算は、販売チャネル別にグルーピングされます。SLOPE、INTCPTおよびRSQRの値は、それぞれ、回帰直線の傾き、切片および確定係数です。(整数)値COUNTは、販売数量データと定価データの両方を使用できる各チャネルの製品の数量です。

SELECT s.channel_id, REGR_SLOPE(s.quantity_sold, p.prod_list_price) SLOPE,
REGR_INTERCEPT(s.quantity_sold, p.prod_list_price) INTCPT,
REGR_R2(s.quantity_sold, p.prod_list_price) RSQR,
REGR_COUNT(s.quantity_sold, p.prod_list_price) COUNT,
REGR_AVGX(s.quantity_sold, p.prod_list_price) AVGLISTP,
REGR_AVGY(s.quantity_sold, p.prod_list_price) AVGQSOLD
FROM sales s, products p WHERE s.prod_id=p.prod_id
AND p.prod_category='Electronics' AND s.time_id=to_DATE('10-OCT-2000')
GROUP BY s.channel_id;

CHANNEL_ID      SLOPE     INTCPT       RSQR      COUNT   AVGLISTP   AVGQSOLD
---------- ---------- ---------- ---------- ---------- ---------- ----------
2          0          1          1         39 466.656667          1
3          0          1          1         60     459.99          1
4          0          1          1         19 526.305789          1

統計集計

Oracleには、一連のSQL統計関数と統計パッケージDBMS_STAT_FUNCSが用意されています。この項では、新しい関数の一部を基本的な構文とともに示します。

DBMS_STAT_FUNCSパッケージの詳細は『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を、構文およびセマンティックについては『Oracle Database SQL言語リファレンス』を参照してください。

記述統計情報

次の記述統計情報を計算できます。

- データセットの中央値

Median (expr) [OVER (query_partition_clause)]

- データセットのモード

STATS_MODE (expr)

仮説の検証 - パラメトリック検定

次の記述統計情報を計算できます。

- 1標本のT-検定

STATS_T_TEST_ONE (expr1, expr2 (a constant) [, return_value])

- 対標本のT-検定

STATS_T_TEST_PAIRED (expr1, expr2 [, return_value])

- 独立標本のT-検定。併合分散

STATS_T_TEST_INDEP (expr1, expr2 [, return_value])

- 独立標本のt-検定。非併合分散

STATS_T_TEST_INDEPU (expr1, expr2 [, return_value])

- F-検定

STATS_F_TEST (expr1, expr2 [, return_value])

- 1方向ANOVA

STATS_ONE_WAY_ANOVA (expr1, expr2 [, return_value])

クロス集計統計情報

次の構文を使用して、クロス集計統計情報を計算できます。

STATS_CROSSTAB (expr1, expr2 [, return_value])

次のいずれかの値が戻されます。

- カイ2乗の測定値

- カイ2乗の測定値の有意性

- カイ2乗の自由度

- ファイ係数、クラメールのV統計

- コンティンジェンシー係数

- コーエンのカッパ

仮説の検証 - ノンパラメトリック検定

次の構文を使用して、仮説統計情報を計算できます。

STATS_BINOMIAL_TEST (expr1, expr2, p [, return_value])

- 2項検定/ウィルコクソンの符号付き順位検定

STATS_WSR_TEST (expr1, expr2 [, return_value])

- マン・ホイットニー検定

STATS_MW_TEST (expr1, expr2 [, return_value])

- コルモゴロフ・スミルノフ検定

STATS_KS_TEST (expr1, expr2 [, return_value])

ノンパラメトリック相関

次のパラメトリック統計情報を計算できます。

- スピアマンのロー係数

CORR_S (expr1, expr2 [, return_value])

- ケンドールのタウb係数

CORR_K (expr1, expr2 [, return_value])

これらの関数に加え、今回のリリースにはPL/SQLパッケージDBMS_STAT_FUNCSも用意されています。このパッケージには、分布適合をサポートする関数とともに、記述統計関数SUMMARYが含まれています。SUMMARY関数は、様々な記述統計情報を含む表の数値列を集計します。分布適合関数は5つあり、それぞれ正規分布、一様分布、ワイブル分布、ポアソン分布、指数分布をサポートします。

ユーザー定義集計

Oracleでは、ユーザー定義集計関数と呼ばれる独自の関数を作成できます。この種の関数は、PL/SQL、JavaおよびCなどのプログラミング言語で記述し、マテリアライズド・ビューで分析関数または集計関数として使用できます。構文および制限の詳細は、『Oracle Databaseデータ・カートリッジ開発者ガイド』を参照してください。

この種の関数のメリットを次に示します。

- きわめて複雑な関数を完全に手続き型言語でプログラミングできます。

- ユーザー定義関数をパラレル処理用にプログラミングする場合に、他のテクニックに比べて高いスケーラビリティが得られます。

- オブジェクトのデータ型を処理できます。

ユーザー定義集計関数の単純な例として、偏りの統計を考えてみます。この計算では、データセットの分散が平均に対して一方に偏っているかどうかが計算されます。分散の一方の最後尾が他方より極端に大きいかどうかが示されます。ユーザー定義集計関数udskewを作成し、前述の例の与信限度額データに適用した場合、SQL文とその結果は次のようになります。

SELECT USERDEF_SKEW(cust_credit_limit) FROM customers
WHERE cust_city='Marshal';

USERDEF_SKEW
============
0.583891

ユーザー定義集計関数を作成する前に、ニーズを通常のSQLで満たすことができるかどうかを考慮する必要があります。SQLでは、特にCASE式を使用すれば、多数の複雑な計算を直接行うことができます。

通常のSQLを使用しても開発を簡素化することは可能です。また、SQLでは多くの問合せ操作がすでに適切にパラレル化されています。前述の例でも、偏りの統計は、長くはなりますが標準的なSQLを使用して作成できます。

ピボット操作

ビジネス・インテリジェンス問合せから戻されたデータは、多くの場合、クロス集計形式で表されていると最も便利に使用できます。SELECT文においてpivot_clauseを使用すると、行を回転して列とするクロス集計の問合せを記述し、回転のプロセスでデータを集計できます。ピボット操作はデータ・ウェアハウスにおける重要なテクニックです。この操作では、入力する複数行がデータ・ウェアハウス内でより少数の行や、より多数の行(通常の場合)に変換されます。ピボット操作時には、ピボット列の値リストにある各項目に集計演算子が適用されます。ピボット列には不定的な式を含めることはできません。式に対するピボットが必要な場合、ビュー内でその式に別名を与えてからPIVOT操作を行います。基本的な構文は次のようになります。

SELECT ....
FROM <table-expr>
PIVOT
(
aggregate-function(<column>)
FOR <pivot-column> IN (<value1>, <value2>,..., <valuen>)
) AS <alias>
WHERE .....

pivot_clause構文については、『Oracle Database SQL言語リファレンス』を参照してください。

ピボット操作の使用方法を示すため、後述の例の基準として次のビューを作成します。

CREATE VIEW sales_view AS
SELECT
prod_name product, country_name country, channel_id channel,
SUBSTR(calendar_quarter_desc, 6,2) quarter,
SUM(amount_sold) amount_sold, SUM(quantity_sold) quantity_sold
FROM sales, times, customers, countries, products
WHERE sales.time_id = times.time_id AND
sales.prod_id = products.prod_id AND
sales.cust_id = customers.cust_id AND
customers.country_id = countries.country_id
GROUP BY prod_name, country_name, channel_id,
SUBSTR(calendar_quarter_desc, 6, 2);

例: ピボット操作

次の文では、channel列を対象として典型的なピボットを解説します。

SELECT * FROM
(SELECT product, channel, amount_sold
FROM sales_view
) S PIVOT (SUM(amount_sold)
FOR CHANNEL IN (3 AS DIRECT_SALES, 4 AS INTERNET_SALES,
5 AS CATALOG_SALES, 9 AS TELESALES))
ORDER BY product;

PRODUCT                  DIRECT_SALES  INTERNET_SALES  CATALOG_SALES  TELESALES
----------------------   ------------  --------------  -------------  ---------
...
Internal 6X CD-ROM          229512.97        26249.55
Internal 8X CD-ROM          286291.49        42809.44
Keyboard Wrist Rest         200959.84        38695.36                   1522.73
...

出力では、ピボット値それぞれについて、DIRECT_SALES、INTERNET_SALES、CATALOG_SALESおよびTELESALESという別名の付いた4つの列が新しく作成されています。この出力には合計が表示されます。別名を付けない場合、列ヘッダーはINリストの値となります。

複数列に対するピボット操作

ピボットは複数の列に対して実行できます。次の文で、典型的な複数列のピボットを解説します。

SELECT *
FROM
(SELECT product, channel, quarter, quantity_sold
FROM sales_view
) PIVOT (SUM(quantity_sold)
FOR (channel, quarter) IN
((5, '02') AS CATALOG_Q2,
(4, '01') AS INTERNET_Q1,
(4, '04') AS INTERNET_Q4,
(2, '02') AS PARTNERS_Q2,
(9, '03') AS TELE_Q3
)
);

PRODUCT              CATALOG_Q2  INTERNET_Q1  INTERNET_Q4  PARTNERS_Q2   TELE_Q3
-------              ----------  -----------  -----------  -----------   -------
...
Bounce                                  347           632          954
...
Smash Up Boxing                         129           280          560
...
Comic Book Heroes                        47           155          275
...

この例では、複数列のINリストが指定され、そのINリストのメンバーに一致するような列ヘッダーが与えられています。

ピボット操作: 複数の集計

ピボット操作では複数の集計を併用できます。この例を次に示します。

SELECT *
FROM
(SELECT product, channel, amount_sold, quantity_sold
FROM sales_view
) PIVOT (SUM(amount_sold) AS sums,
SUM(quantity_sold) AS sumq
FOR channel IN (5, 4, 2, 9)
)
ORDER BY product;

PRODUCT                5_SUMS  5_SUMQ    4_SUMS   4_SUMQ      2_SUMS   2_SUMQ    9_SUMS   9_SUMQ
-------------          ------  ------    ------   ------      ------   ------    ------   ------
O/S Doc Set English                   142780.36     3081   381397.99     8044   6028.66      134
O/S Doc Set French                     55503.58     1192   132000.77     2782
...

この問合せでは、ピボットの値(または別名)と集計関数の別名をアンダースコアで連結することにより、列ヘッダーが作成されます。

ソース・データ内のNULLとPIVOTで生成されたNULLとの識別

ソース・データ内に存在するNULL値と、PIVOTを使用することで生成されたNULL値は、識別できます。次の例では、PIVOTによって生成されるNULLについて解説します。

次の問合せでは5列分の行が戻されます。列prod_idと、ピボット操作結果の列Q1、Q1_COUNT_TOTAL、Q2、Q2_COUNT_TOTALの5列です。prod_idの一意の各値について、Q1_COUNT_TOTALには行のqtrの値がQ1である行の合計数が戻されます。Q2_COUNT_TOTALには行qtrの値がQ2である行の合計数が戻されます。

ここでは、次の構造からなるsales2という表があると仮定します。

PROD_ID   QTR  AMOUNT_SOLD
-------   ---  -----------
100        Q1           10
100        Q1           20
100        Q2         NULL
200        Q1           50

SELECT *
FROM sales2
PIVOT
( SUM(amount_sold), COUNT(*) AS count_total
FOR qtr IN ('Q1', 'Q2')
);

PROD_ID    "Q1"   "Q1_COUNT_TOTAL"  "Q2"       "Q2_COUNT_TOTAL"
-------    ----   ----------------  ---------  ----------------
100      20                  2   NULL <1>                 1
200      50                  1   NULL <2>                 0

この結果から、prod_idが100の場合、四半期(quarter)がQ1の売上行が2行、四半期がQ2の売上行が1行あること、prod_idが200の場合、四半期がQ1の売上行が1行、四半期がQ2の売上行はないことがわかります。したがって、Q2_COUNT_TOTALの内容から、NULL<1>は元の表内の行のうちメジャーがNULL値のものに由来し、NULL<2>は元の表内でprod_idが200の行が四半期Q2にはなかったために表示されていると識別できます。

アンピボット操作

アンピボット操作では、PIVOT操作を元に戻しません。かわりに、データを列から行に回転します。ピボットされたデータを操作している場合にUNPIVOT操作を行っても、PIVOTまたはその他の手段で作成された集計は元に戻せません。

アンピボットを解説するため、まず、所定の年の四半期を示す4列からなる、ピボットされた表を作成します。

CREATE TABLE pivotedTable AS
SELECT *
FROM (SELECT product, quarter, quantity_sold, amount_sold
FROM sales_view)
PIVOT
(
SUM(quantity_sold) AS sumq, SUM(amount_sold) AS suma
FOR quarter IN ('01' AS Q1, '02' AS Q2, '03' AS Q3, '04' AS Q4));

表の内容は次のようになります。

SELECT *
FROM pivotedTable
ORDER BY product;

PRODUCT         Q1_SUMQ  Q1_SUMA  Q2_SUMQ  Q2_SUMA   Q3_SUMQ  Q3_SUMA   Q4_SUMQ    Q4_SUMA
--------------- -------  -------  -------  --------  -------  --------  -------    ---------
1.44MB External   6098   58301.33    5112   49001.56    6050   56974.3     5848     55341.28
128MB Memory      1963  110763.63    2361  132123.12    3069  170710.4     2832    157736.6
17" LCD           1492 1812786.94    1387 1672389.06    1591 1859987.66    1540   1844008.11
...

次のUNPIVOT操作では、一連の四半期(quarter)の列が回転されて行になります。各製品について、各四半期を表す4つの行が作成されます。

SELECT product, DECODE(quarter, 'Q1_SUMQ', 'Q1', 'Q2_SUMQ', 'Q2', 'Q3_SUMQ', 'Q3',
'Q4_SUMQ', 'Q4') AS quarter, quantity_sold
FROM  pivotedTable
UNPIVOT INCLUDE NULLS
(quantity_sold
FOR quarter IN (Q1_SUMQ, Q2_SUMQ, Q3_SUMQ, Q4_SUMQ))
ORDER BY product, quarter;

PRODUCT                          QU    QUANTITY_SOLD
-------                          --    -------------
1.44MB External 3.5" Diskette    Q1             6098
1.44MB External 3.5" Diskette    Q2             5112
1.44MB External 3.5" Diskette    Q3             6050
1.44MB External 3.5" Diskette    Q4             5848
128MB Memory Card                Q1             1963
128MB Memory Card                Q2             2361
128MB Memory Card                Q3             3069
128MB Memory Card                Q4             2832
...

この例ではINCLUDE NULLSが使用されています。デフォルト設定のEXCLUDE NULLSを使用することもできます。

さらに、次のように、2つの列を使用してアンピボット操作を行うことができます。

SELECT product, quarter, quantity_sold, amount_sold
FROM  pivotedTable
UNPIVOT INCLUDE NULLS
(
(quantity_sold, amount_sold)
FOR quarter IN ((Q1_SUMQ, Q1_SUMA) AS 'Q1', (Q2_SUMQ, Q2_SUMA) AS 'Q2', (Q3_SUMQ, Q3_SUMA) AS 'Q3', (Q4_SUMQ, Q4_SUMA) AS 'Q4'))
ORDER BY product, quarter;

PRODUCT                          QU   QUANTITY_SOLD   AMOUNT_SOLD
-----------------------------    --   -------------   ------------
1.44MB External 3.5" Diskette    Q1            6098       58301.33
1.44MB External 3.5" Diskette    Q2            5112       49001.56
1.44MB External 3.5" Diskette    Q3            6050       56974.3
1.44MB External 3.5" Diskette    Q4            5848       55341.28
128MB Memory Card                Q1            1963      110763.63
128MB Memory Card                Q2            2361      132123.12
128MB Memory Card                Q3            3069      170710.4
128MB Memory Card                Q4            2832      157736.6

ワイルド・カードおよび副問合せのXML操作によるピボット操作

ピボットの対象とする列でワイルド・カード引数または副問合せを使用するには、PIVOT XML構文を利用する方法があります。PIVOT XMLを使用した場合、操作の出力は適切に整形されたXMLになります。

次の例に、ワイルド・カード・キーワードANYを使用した場合を示します。この例では、sales_view内のチャネルの値をすべて含むXMLが出力されます。

SELECT *
FROM
(SELECT product, channel, quantity_sold
FROM sales_view
) PIVOT XML(SUM(quantity_sold)
FOR channel IN (ANY)
);

キーワードANYは、PIVOT操作においてはXML操作の一部としてのみ利用できます。この出力には、データセット内にチャネルが存在する場合のデータが含まれます。また、複数の値を戻すには集計関数ではGROUP BY句を指定する必要がありますが、pivot_clauseでは明示的なGROUP BY句は含まれていません。かわりに、pivot_clauseでは暗黙的なGROUP BYを実行します。

次の例では、副問合せを使用する場合を示します。この例では、すべてのチャネルの値と、各チャネルに対応する売上データを含むXMLが出力されます。

SELECT *
FROM
(SELECT product, channel, quantity_sold
FROM sales_view
) PIVOT XML(SUM(quantity_sold)
FOR channel IN (SELECT DISTINCT channel_id FROM CHANNELS)
);

この出力ではデータが稠密化され、各製品についてあり得るチャネルがすべて含まれる状態となります。

レポート用のデータの稠密化

データは通常、スパースな形式で格納されています。つまり、ディメンション値の特定の組合せで値が存在しない場合、ファクト表には行が存在していません。しかし、ファクト・データが存在しない場合でも、ディメンション値のすべての組合せの行を表示し、データを稠密な形式で表示する必要がある場合があります。たとえば、特定の期間において製品が販売されていない場合でも、その期間の売上値をゼロとして製品の横に表示する場合などです。さらに、データが時間ディメンションに沿って稠密であれば、時系列の計算を簡単に実行できます。これは、稠密なデータが期間ごとに一定数の行を占めているため、物理オフセットを指定した分析ウィンドウ関数の使用が単純化されるためです。データの稠密化は、スパース・データを稠密な形式に変換するプロセスです。スパース性の問題を解決するために、パーティション外部結合を使用して時系列間または他のディメンションとの間のギャップを埋めることができます。この結合は、問合せで定義した各論理パーティションに外部結合を適用することで、従来の外部結合の構文を拡張したものです。Oracleでは、PARTITION BY句で指定した式に基づいて、問合せの行を論理的にパーティション化します。パーティション外部結合の結果は、論理的にパーティション化された表にある各パーティションの外部結合と、結合のもう一方の表のUNIONです。

このタイプの結合を使用すると、時間ディメンションに限らず、あらゆるディメンションのギャップを埋めることができます。比較の基準として最も頻繁に使用されるのは時間ディメンションであるため、ここにあげた例のほとんどでは、時間のディメンションを対象としています。

パーティション結合の構文

パーティション外部結合の構文は、ANSI SQLのJOIN句にPARTITION BY句と式のリストを追加したものです。リストの式は、外部結合が適用されるグループを指定します。次に、パーティション外部結合で通常使用する2種類の構文を示します。

SELECT .....
FROM table_reference
PARTITION BY (expr [, expr ]... )
RIGHT OUTER JOIN table_reference

SELECT .....
FROM table_reference
LEFT OUTER JOIN table_reference
PARTITION BY {expr [,expr ]...)

FULL OUTER JOINは、パーティション外部結合ではサポートされないので注意してください。

スパースなデータの例

標準的なスパース・ディメンションの状態を次の例に示します。2000から2001年の20から30週における製品Bounceの1週間の売上と年度累計の売上が計算されます。

SELECT SUBSTR(p.Prod_Name,1,15) Product_Name, t.Calendar_Year Year,
t.Calendar_Week_Number Week, SUM(Amount_Sold) Sales
FROM Sales s, Times t, Products p
WHERE s.Time_id = t.Time_id AND s.Prod_id = p.Prod_id AND
p.Prod_name IN ('Bounce') AND t.Calendar_Year IN (2000,2001) AND
t.Calendar_Week_Number BETWEEN 20 AND 30
GROUP BY p.Prod_Name, t.Calendar_Year, t.Calendar_Week_Number;

PRODUCT_NAME          YEAR       WEEK      SALES
--------------- ---------- ---------- ----------
Bounce                2000         20        801
Bounce                2000         21    4062.24
Bounce                2000         22    2043.16
Bounce                2000         23    2731.14
Bounce                2000         24    4419.36
Bounce                2000         27    2297.29
Bounce                2000         28    1443.13
Bounce                2000         29    1927.38
Bounce                2000         30    1927.38
Bounce                2001         20    1483.3
Bounce                2001         21    4184.49
Bounce                2001         22    2609.19
Bounce                2001         23    1416.95
Bounce                2001         24    3149.62
Bounce                2001         25    2645.98
Bounce                2001         27    2125.12
Bounce                2001         29    2467.92
Bounce                2001         30    2620.17

この例では、データが稠密化されていれば22行(1年11週間の2年分)になるはずです。しかし、2000年の25週と26週、2001年の26週と28週がないので、実際には18行のみです。

データのギャップ補完

前の問合せのスパース・データを取り、時間データの稠密なセットでパーティション外部結合を実行できます。次の問合せでは、元の問合せをvという別名にし、tという別名にしたtimes表からデータを選択します。ここでは、時系列にギャップはないので、22行を取得します。追加された4行には、NVL関数で設定されたSales値0があります。

SELECT Product_Name, t.Year, t.Week, NVL(Sales,0) dense_sales
FROM
(SELECT SUBSTR(p.Prod_Name,1,15) Product_Name,
t.Calendar_Year Year, t.Calendar_Week_Number Week, SUM(Amount_Sold) Sales
FROM Sales s, Times t, Products p
WHERE s.Time_id = t.Time_id AND s.Prod_id = p.Prod_id AND
p.Prod_name IN ('Bounce') AND t.Calendar_Year IN (2000,2001) AND
t.Calendar_Week_Number BETWEEN 20 AND 30
GROUP BY p.Prod_Name, t.Calendar_Year, t.Calendar_Week_Number) v
PARTITION BY (v.Product_Name)
RIGHT OUTER JOIN
(SELECT DISTINCT Calendar_Week_Number Week, Calendar_Year Year
FROM Times
WHERE Calendar_Year IN (2000, 2001)
AND Calendar_Week_Number BETWEEN 20 AND 30) t
ON (v.week = t.week AND v.Year = t.Year)
ORDER BY t.year, t.week;

PRODUCT_NAME          YEAR       WEEK DENSE_SALES
--------------- ---------- ---------- -----------
Bounce                2000         20         801
Bounce                2000         21     4062.24
Bounce                2000         22     2043.16
Bounce                2000         23     2731.14
Bounce                2000         24     4419.36
Bounce                2000         25           0
Bounce                2000         26           0
Bounce                2000         27     2297.29
Bounce                2000         28     1443.13
Bounce                2000         29     1927.38
Bounce                2000         30     1927.38
Bounce                2001         20      1483.3
Bounce                2001         21     4184.49
Bounce                2001         22     2609.19
Bounce                2001         23     1416.95
Bounce                2001         24     3149.62
Bounce                2001         25     2645.98
Bounce                2001         26           0
Bounce                2001         27     2125.12
Bounce                2001         28           0
Bounce                2001         29     2467.92
Bounce                2001         30     2620.17

この問合せでは、時間ディメンションのインライン・ビューに、20から30の週のWHERE条件が置かれています。これは、結果セットのサイズを大きくしないために導入されたものです。

2つのディメンションのギャップ補完

n次元のデータは通常、(n-2)ページのディメンションの稠密な2次元のクロス集計として表示されます。これには、クロス集計内にある2つのディメンションのディメンション値をすべて埋める必要があります。次に示す例では、2つのディメンションのギャップを埋めるために、パーティション外部結合機能が使用されています。

WITH v1 AS
(SELECT p.prod_id, country_id, calendar_year,
SUM(quantity_sold) units, SUM(amount_sold) sales
FROM sales s, products p, customers c, times t
WHERE s.prod_id in (147, 148) AND t.time_id = s.time_id AND
c.cust_id = s.cust_id AND p.prod_id = s.prod_id
GROUP BY p.prod_id, country_id, calendar_year),
v2 AS                                  --countries to use for densifications
(SELECT DISTINCT country_id
FROM customers
WHERE country_id IN (52782, 52785, 52786, 52787, 52788)),
v3 AS                                   --years to use for densifications
(SELECT DISTINCT calendar_year FROM times)
SELECT v4.prod_id, v4.country_id, v3.calendar_year, units, sales
FROM
(SELECT prod_id, v2.country_id, calendar_year, units, sales
FROM v1 PARTITION BY (prod_id)
RIGHT OUTER JOIN v2                  --densifies on country
ON (v1.country_id = v2.country_id)) v4
PARTITION BY (prod_id,country_id)
RIGHT OUTER JOIN v3                     --densifies on year
ON (v4.calendar_year = v3.calendar_year)
ORDER BY 1, 2, 3;

この問合せでは、WITH副問合せのファクタリング句v1で、製品、国、年レベルの売上データを集計します。この結果はスパースですが、製品ごとのすべての国、年の組合せを表示する場合もあります。このために、製品の値に基づいたv1の各パーティションを取り、最初に国ディメンションでそれを外部結合します。これによって、製品ごとの国の値がすべて提供されます。次に、結果を取り、それを製品と国の値でパーティション化し、さらに時間ディメンションで外部結合します。これで、製品と国の組合せごとに時間の値がすべて提供されます。

PROD_ID COUNTRY_ID CALENDAR_YEAR      UNITS      SALES
---------- ---------- ------------- ---------- ----------
147      52782          1998
147      52782          1999         29     209.82
147      52782          2000         71     594.36
147      52782          2001        345    2754.42
147      52782          2002
147      52785          1998          1       7.99
147      52785          1999
147      52785          2000
147      52785          2001
147      52785          2002
147      52786          1998          1       7.99
147      52786          1999
147      52786          2000          2      15.98
147      52786          2001
147      52786          2002
147      52787          1998
147      52787          1999
147      52787          2000
147      52787          2001
147      52787          2002
147      52788          1998
147      52788          1999
147      52788          2000          1       7.99
147      52788          2001
147      52788          2002
148      52782          1998        139    4046.67
148      52782          1999        228    5362.57
148      52782          2000        251    5629.47
148      52782          2001        308    7138.98
148      52782          2002
148      52785          1998
148      52785          1999
148      52785          2000
148      52785          2001
148      52785          2002
148      52786          1998
148      52786          1999
148      52786          2000
148      52786          2001
148      52786          2002
148      52787          1998
148      52787          1999
148      52787          2000
148      52787          2001
148      52787          2002
148      52788          1998          4     117.23
148      52788          1999
148      52788          2000
148      52788          2001
148      52788          2002

在庫表のギャップ補完

在庫表は通常、様々な製品の有効単位数を追跡します。この表は、イベントが発生した場合にのみ製品の行を格納するため、スパースです。sales表の場合、イベントは売上であり、在庫表の場合、イベントは製品の有効数量の変化です。たとえば、次の在庫表を考えてみます。

CREATE TABLE invent_table (
product VARCHAR2(10),
time_id DATE,
quant NUMBER);

INSERT INTO invent_table VALUES
('bottle', TO_DATE('01/04/01', 'DD/MM/YY'), 10);
INSERT INTO invent_table VALUES
('bottle', TO_DATE('06/04/01', 'DD/MM/YY'), 8);
INSERT INTO invent_table VALUES
('can', TO_DATE('01/04/01', 'DD/MM/YY'), 15);
INSERT INTO invent_table VALUES
('can', TO_DATE('04/04/01', 'DD/MM/YY'), 11);

在庫表には、次の行があります。

PRODUCT    TIME_ID   QUANT
---------- --------- -----
bottle     01-APR-01    10
bottle     06-APR-01     8
can        01-APR-01    15
can        04-APR-01    11

レポートの目的で、この在庫データを異なる表示にする場合があります。たとえば、各製品の時間の値をすべて表示する必要があるような場合です。これには、パーティション外部結合を使用します。さらに、存在しない期間で新たに挿入された行については、最も近い既存の期間から持ち越された数量列の値を表示する場合があります。これには、分析ウィンドウ関数LAST_VALUEの値を使用します。次に、問合せとその目的とする出力を示します。

WITH v1 AS
(SELECT time_id
FROM times
WHERE times.time_id BETWEEN
TO_DATE('01/04/01', 'DD/MM/YY')
AND TO_DATE('07/04/01', 'DD/MM/YY'))
SELECT product, time_id, quant quantity,
LAST_VALUE(quant IGNORE NULLS)
OVER (PARTITION BY product ORDER BY time_id)
repeated_quantity
FROM
(SELECT product, v1.time_id, quant
FROM invent_table PARTITION BY (product)
RIGHT OUTER JOIN v1
ON (v1.time_id = invent_table.time_id))
ORDER BY 1, 2;

内部問合せは、各製品内の時間でパーティション外部結合を計算します。内部問合せは、時間ディメンションでデータを稠密化します(つまり、時間ディメンションは、週の各曜日の行を持つことになります)。ただし、メジャー列quantityは、新たに追加された行に対してNULLとなります(次の結果のquantity列の出力を参照してください)。

外部問合せでは、分析関数LAST_VALUEを使用しています。この関数を適用すると、製品単位でデータがパーティション化され、時間ディメンション列(time_id)でデータが順序付けられます。行ごとに、この関数は、IGNORE NULLSオプションを基に、ウィンドウ内で最後の非NULL値を検索します。このオプションは、LAST_VALUEとFIRST_VALUEの両方で使用できます。次の結果では、repeated_quantity列に目的の出力が表示されます。

PRODUCT    TIME_ID   QUANTITY REPEATED_QUANTITY
---------- --------- -------- -----------------
bottle     01-APR-01       10                10
bottle     02-APR-01                         10
bottle     03-APR-01                         10
bottle     04-APR-01                         10
bottle     05-APR-01                         10
bottle     06-APR-01        8                 8
bottle     07-APR-01                          8
can        01-APR-01       15                15
can        02-APR-01                         15
can        03-APR-01                         15
can        04-APR-01       11                11
can        05-APR-01                         11
can        06-APR-01                         11
can        07-APR-01                         11

ギャップを埋めるデータ値の計算

前の項の例では、パーティション外部結合を使用して1つ以上のディメンションのギャップを埋める方法を示しています。ただし、パーティション外部結合で生成された結果セットでは、PARTITION BYリストに含まれない列にNULL値があります。通常、これらはメジャー列です。分析SQL関数を使用すると、こうしたNULL値を非NULL値に置き換えることができます。

たとえば、次の問合せは、2000年の64MBメモリー・カードおよびDVD-Rディスク(製品IDは122と136)の月の売上合計を計算します。この問合せでは、すべての月のデータを稠密化するために、パーティション外部結合を使用します。売上が存在しない月に対しては、分析SQL関数AVGを使用して、製品の売上が存在する各月の平均売上と平均数量を計算します。

SQL*Plusで作業している場合は、次の2つのコマンドを実行すると、列ヘッダーが折り返されて結果が読みやすくなります。

col computed_units  heading  'Computed|_units'
col computed_sales  heading  'Computed|_sales'

WITH V AS
(SELECT substr(p.prod_name,1,12) prod_name, calendar_month_desc,
SUM(quantity_sold) units, SUM(amount_sold) sales
FROM sales s, products p, times t
WHERE s.prod_id IN (122,136) AND calendar_year = 2000
AND t.time_id = s.time_id
AND p.prod_id = s.prod_id
GROUP BY p.prod_name, calendar_month_desc)
SELECT v.prod_name, calendar_month_desc, units, sales,
NVL(units, AVG(units) OVER (partition by v.prod_name)) computed_units,
NVL(sales, AVG(sales) OVER (partition by v.prod_name)) computed_sales
FROM
(SELECT DISTINCT calendar_month_desc
FROM times
WHERE calendar_year = 2000) t
LEFT OUTER JOIN V
PARTITION BY (prod_name)
USING (calendar_month_desc);
computed   computed
PROD_NAME    CALENDAR      UNITS      SALES     _units     _sales
------------ -------- ---------- ---------- ---------- ----------
64MB Memory  2000-01         112    4129.72        112    4129.72
64MB Memory  2000-02         190       7049        190       7049
64MB Memory  2000-03          47    1724.98         47    1724.98
64MB Memory  2000-04          20      739.4         20      739.4
64MB Memory  2000-05          47    1738.24         47    1738.24
64MB Memory  2000-06          20      739.4         20      739.4
64MB Memory  2000-07                        72.6666667    2686.79
64MB Memory  2000-08                        72.6666667    2686.79
64MB Memory  2000-09                        72.6666667    2686.79
64MB Memory  2000-10                        72.6666667    2686.79
64MB Memory  2000-11                        72.6666667    2686.79
64MB Memory  2000-12                        72.6666667    2686.79
DVD-R Discs, 2000-01         167     3683.5        167     3683.5
DVD-R Discs, 2000-02         152    3362.24        152    3362.24
DVD-R Discs, 2000-03         188    4148.02        188    4148.02
DVD-R Discs, 2000-04         144    3170.09        144    3170.09
DVD-R Discs, 2000-05         189    4164.87        189    4164.87
DVD-R Discs, 2000-06         145    3192.21        145    3192.21
DVD-R Discs, 2000-07                            124.25    2737.71
DVD-R Discs, 2000-08                            124.25    2737.71
DVD-R Discs, 2000-09           1      18.91          1      18.91
DVD-R Discs, 2000-10                            124.25    2737.71
DVD-R Discs, 2000-11                            124.25    2737.71
DVD-R Discs, 2000-12           8     161.84          8     161.84

稠密化したデータに対する時系列の計算

稠密化は、レポートのみを目的としたものではありません。稠密化によって、一部の計算、特に時系列の計算が可能となります。時系列の計算は、データが時間ディメンションに沿って稠密であれば、より簡単になります。稠密なデータは、期間ごとに一定数の行を占めているため、物理オフセットを指定した分析ウィンドウ関数の使用が単純化されます。

実例として、「データのギャップ補完」に示す例を使用し、その問合せに分析関数を追加します。次の機能強化した例では、週の売上とともに、週次年度累計売上を計算します。時系列の稠密化においてパーティション外部結合により挿入されたNULL値は、通常どおりに処理されます。つまり、SUM関数はこの値を0として処理します。

SELECT Product_Name, t.Year, t.Week, NVL(Sales,0) Current_sales,
SUM(Sales)
OVER (PARTITION BY Product_Name, t.year ORDER BY t.week) Cumulative_sales
FROM
(SELECT SUBSTR(p.Prod_Name,1,15) Product_Name, t.Calendar_Year Year,
t.Calendar_Week_Number Week, SUM(Amount_Sold) Sales
FROM Sales s, Times t, Products p
WHERE s.Time_id = t.Time_id AND
s.Prod_id = p.Prod_id AND p.Prod_name IN ('Bounce') AND
t.Calendar_Year IN (2000,2001) AND
t.Calendar_Week_Number BETWEEN 20 AND 30
GROUP BY p.Prod_Name, t.Calendar_Year, t.Calendar_Week_Number) v
PARTITION BY (v.Product_Name)
RIGHT OUTER JOIN
(SELECT DISTINCT
Calendar_Week_Number Week, Calendar_Year Year
FROM Times
WHERE Calendar_Year in (2000, 2001)
AND Calendar_Week_Number BETWEEN 20 AND 30) t
ON (v.week = t.week AND v.Year = t.Year)
ORDER BY t.year, t.week;

PRODUCT_NAME          YEAR       WEEK CURRENT_SALES CUMULATIVE_SALES
--------------- ---------- ---------- ------------- ----------------
Bounce                2000         20           801              801
Bounce                2000         21       4062.24          4863.24
Bounce                2000         22       2043.16           6906.4
Bounce                2000         23       2731.14          9637.54
Bounce                2000         24       4419.36          14056.9
Bounce                2000         25             0          14056.9
Bounce                2000         26             0          14056.9
Bounce                2000         27       2297.29         16354.19
Bounce                2000         28       1443.13         17797.32
Bounce                2000         29       1927.38          19724.7
Bounce                2000         30       1927.38         21652.08
Bounce                2001         20        1483.3           1483.3
Bounce                2001         21       4184.49          5667.79
Bounce                2001         22       2609.19          8276.98
Bounce                2001         23       1416.95          9693.93
Bounce                2001         24       3149.62         12843.55
Bounce                2001         25       2645.98         15489.53
Bounce                2001         26             0         15489.53
Bounce                2001         27       2125.12         17614.65
Bounce                2001         28             0         17614.65
Bounce                2001         29       2467.92         20082.57
Bounce                2001         30       2620.17         22702.74

1つの時間レベルでの周期ごとの比較: 例

複数の期間にわたって値を比較するには、この方法をどのように使用しますか。特に、週レベルで複数年度にわたる売上の比較については、どのように計算しますか。次の問合せは、同じ行で、2000年と2001年の各製品の週次年度累計売上を戻します。

この例では、はじめにWITH句がきています。これによって、問合せの読みやすさが向上し、パーティション外部結合に集中できます。SQL*Plusで作業している場合は、次のコマンドを実行すると、列ヘッダーが折り返されて結果が読みやすくなります。

col Weekly_ytd_sales_prior_year  heading  'Weekly_ytd|_sales_|prior_year'

WITH v AS
(SELECT SUBSTR(p.Prod_Name,1,6) Prod, t.Calendar_Year Year,
t.Calendar_Week_Number Week, SUM(Amount_Sold) Sales
FROM Sales s, Times t, Products p
WHERE s.Time_id = t.Time_id AND
s.Prod_id = p.Prod_id AND p.Prod_name in ('Y Box') AND
t.Calendar_Year in (2000,2001) AND
t.Calendar_Week_Number BETWEEN 30 AND 40
GROUP BY p.Prod_Name, t.Calendar_Year, t.Calendar_Week_Number)
SELECT Prod , Year, Week, Sales,
Weekly_ytd_sales, Weekly_ytd_sales_prior_year
FROM
(SELECT Prod, Year, Week, Sales, Weekly_ytd_sales,
LAG(Weekly_ytd_sales, 1) OVER
(PARTITION BY Prod , Week ORDER BY Year) Weekly_ytd_sales_prior_year
FROM
(SELECT v.Prod  Prod , t.Year Year, t.Week Week,
NVL(v.Sales,0) Sales, SUM(NVL(v.Sales,0)) OVER
(PARTITION BY v.Prod , t.Year ORDER BY t.week) weekly_ytd_sales
FROM v
PARTITION BY (v.Prod )
RIGHT OUTER JOIN
(SELECT DISTINCT Calendar_Week_Number Week, Calendar_Year Year
FROM Times
WHERE Calendar_Year IN (2000, 2001)) t
ON (v.week = t.week AND v.Year = t.Year)
) dense_sales
) year_over_year_sales
WHERE Year = 2001 AND Week BETWEEN 30 AND 40
ORDER BY 1, 2, 3;
Weekly_ytd
_sales_
PROD         YEAR       WEEK      SALES WEEKLY_YTD_SALES prior_year
------ ---------- ---------- ---------- ---------------- ----------
Y Box        2001         30    7877.45          7877.45          0
Y Box        2001         31   13082.46         20959.91    1537.35
Y Box        2001         32   11569.02         32528.93    9531.57
Y Box        2001         33   38081.97          70610.9   39048.69
Y Box        2001         34   33109.65        103720.55   69100.79
Y Box        2001         35          0        103720.55   71265.35
Y Box        2001         36     4169.3        107889.85   81156.29
Y Box        2001         37   24616.85         132506.7   95433.09
Y Box        2001         38   37739.65        170246.35  107726.96
Y Box        2001         39     284.95         170531.3   118817.4
Y Box        2001         40   10868.44        181399.74  120969.69

インライン・ビューdense_salesのFROM句では、集計ビューvと時間ビューtのパーティション外部結合を使用して、時間ディメンションに沿って売上データのギャップを埋めます。パーティション外部結合の結果が、分析関数SUM ... OVERで処理され、週次年度累計売上(weekly_ytd_sales列)が計算されます。したがって、dense_salesビューは、集計ビューにないものも含めて、週次年度累計売上データを計算します。次に、インライン・ビューyear_over_year_salesは、LAG関数を使用して、1年前の週次年度累計売上を計算します。weekly_ytd_sales_prior_yearというラベルが付けられたLAG関数には、2000年と2001年の同じ週の行を単一のパーティションに組み合せるPARTITION BY句を指定します。1のオフセットをLAG関数に渡し、前年の週次年度累計売上を取得します。最も外側の問合せブロックでは、条件yr = 2001でyear_over_year_salesのデータを選択します。したがって、この問合せでは各製品について、2001年と2000年の指定した週の週次年度累計売上が戻されます。

複数の時間レベルでの周期ごとの比較: 例

前の例では、単一の時間レベルでの比較を作成する方法を示しましたが、1回の問合せで複数の時間レベルを処理できればより便利となります。たとえば、年、四半期、月、曜日レベルで前期との売上を比較できます。ここでは、時間階層のすべてのレベルで、年度累計売上の年度ごとの比較を実行する問合せの作成方法について説明します。

このタスクを実行するには、いくつかの手順を踏みます。目標は、1回の問合せで日、週、月、四半期、年レベルでの比較を実行することです。手順は次のとおりです。

1. cube_prod_timeというビューを作成します。このビューは、timesとproductsにわたって集計した売上の階層的キューブを保持します。

2. 次に、キューブのエッジとして使用する時間ディメンションのビューを作成します。完全な日付セットを保持する時間エッジは、cube_prod_timeビューでスパース・データにパーティション外部結合されます。

3. 最後に、パフォーマンスを最大にするために、マテリアライズド・ビューmv_prod_timeを作成します。これにはcube_prod_timeと同じディメンションを使用します。

階層的キューブの詳細は、第21章「データ・ウェアハウスにおける集計のためのSQL」を参照してください。マテリアライズド・ビューは、次の項の手順1で定義します。

手順1   階層的キューブ・ビューの作成

次に示すマテリアライズド・ビューは、すでにシステムに存在している場合もあります。存在していない場合は、ここで作成します。作成する場合は、処理時間をかけないように、問合せでは2つの製品に制限するので注意してください。

CREATE OR REPLACE VIEW cube_prod_time AS
SELECT
(CASE
WHEN ((GROUPING(calendar_year)=0 )
AND (GROUPING(calendar_quarter_desc)=1 ))
THEN (TO_CHAR(calendar_year) || '_0')
WHEN ((GROUPING(calendar_quarter_desc)=0 )
AND (GROUPING(calendar_month_desc)=1 ))
THEN (TO_CHAR(calendar_quarter_desc) || '_1')
WHEN ((GROUPING(calendar_month_desc)=0 )
AND (GROUPING(t.time_id)=1 ))
THEN (TO_CHAR(calendar_month_desc) || '_2')
ELSE (TO_CHAR(t.time_id) || '_3')
END) Hierarchical_Time,
calendar_year year, calendar_quarter_desc quarter,
calendar_month_desc month, t.time_id day,
prod_category cat, prod_subcategory subcat, p.prod_id prod,
GROUPING_ID(prod_category, prod_subcategory, p.prod_id,
calendar_year, calendar_quarter_desc, calendar_month_desc,t.time_id) gid,
GROUPING_ID(prod_category, prod_subcategory, p.prod_id) gid_p,
GROUPING_ID(calendar_year, calendar_quarter_desc,
calendar_month_desc, t.time_id) gid_t,
SUM(amount_sold) s_sold, COUNT(amount_sold) c_sold, COUNT(*) cnt
FROM SALES s, TIMES t, PRODUCTS p
WHERE s.time_id = t.time_id AND
p.prod_name IN ('Bounce', 'Y Box') AND s.prod_id = p.prod_id
GROUP BY
ROLLUP(calendar_year, calendar_quarter_desc, calendar_month_desc, t.time_id),
ROLLUP(prod_category, prod_subcategory, p.prod_id);

このビューは2つの製品に限定されるため、2200強の行のみが戻されます。Hierarchical_Time列には、時間階層の全レベルの文字列表現があるので注意が必要です。Hierarchical_Time列で使用されるCASE式は、マーカー(_0, _1, ...)を各日付文字列に付加して、値の時間レベルを示します。_0は年レベルを表し、_1は四半期レベル、_2は月レベル、_3は日レベルを表します。GROUP BY句は、時間ディメンションおよび製品ディメンションのロールアップ階層を指定する、連結されたROLLUPです。GROUP BY句によって、階層的キューブの内容が決まります。

手順2   日付値の完全なセットであるビューedge_timeの作成

edge_timeを使用して、パーティション外部結合による階層的キューブの時間のギャップを埋めます。edge_timeのHierarchical_Time列が、ビューcube_prod_timeのHierarchical_Time列とのパーティション結合に使用されます。edge_timeは次の文で定義します。

CREATE OR REPLACE VIEW edge_time AS
SELECT
(CASE
WHEN ((GROUPING(calendar_year)=0 )
AND (GROUPING(calendar_quarter_desc)=1 ))
THEN (TO_CHAR(calendar_year) || '_0')
WHEN ((GROUPING(calendar_quarter_desc)=0 )
AND (GROUPING(calendar_month_desc)=1 ))
THEN (TO_CHAR(calendar_quarter_desc) || '_1')
WHEN ((GROUPING(calendar_month_desc)=0 )
AND (GROUPING(time_id)=1 ))
THEN (TO_CHAR(calendar_month_desc) || '_2')
ELSE (TO_CHAR(time_id) || '_3')
END) Hierarchical_Time,
calendar_year yr, calendar_quarter_number qtr_num,
calendar_quarter_desc qtr, calendar_month_number mon_num,
calendar_month_desc mon, time_id - TRUNC(time_id, 'YEAR') + 1 day_num,
time_id day,
GROUPING_ID(calendar_year, calendar_quarter_desc,
calendar_month_desc, time_id) gid_t
FROM TIMES
GROUP BY ROLLUP
(calendar_year, (calendar_quarter_desc, calendar_quarter_number),
(calendar_month_desc, calendar_month_number), time_id);

手順3   パフォーマンス向上をサポートするマテリアライズド・ビューmv_prod_timeの作成

このマテリアライズド・ビューの定義は、これまでに定義したビューcube_prod_timeの定義と同じです。同じ問合せとなるので、cube_prod_timeへの参照はマテリアライズド・ビューmv_prod_timeを使用するように書き換えられます。次のマテリアライズド・ビューは、すでにシステムに存在している場合もあります。存在していない場合は、ここで作成します。作成が必要な場合は、処理時間をかけないように、問合せでは2つの製品に制限するので注意してください。

CREATE MATERIALIZED VIEW mv_prod_time
REFRESH COMPLETE ON DEMAND AS
SELECT
(CASE
WHEN ((GROUPING(calendar_year)=0 )
AND (GROUPING(calendar_quarter_desc)=1 ))
THEN (TO_CHAR(calendar_year) || '_0')
WHEN ((GROUPING(calendar_quarter_desc)=0 )
AND (GROUPING(calendar_month_desc)=1 ))
THEN (TO_CHAR(calendar_quarter_desc) || '_1')
WHEN ((GROUPING(calendar_month_desc)=0 )
AND (GROUPING(t.time_id)=1 ))
THEN (TO_CHAR(calendar_month_desc) || '_2')
ELSE (TO_CHAR(t.time_id) || '_3')
END) Hierarchical_Time,
calendar_year year, calendar_quarter_desc quarter,
calendar_month_desc month, t.time_id day,
prod_category cat, prod_subcategory subcat, p.prod_id prod,
GROUPING_ID(prod_category, prod_subcategory, p.prod_id,
calendar_year, calendar_quarter_desc, calendar_month_desc,t.time_id) gid,
GROUPING_ID(prod_category, prod_subcategory, p.prod_id) gid_p,
GROUPING_ID(calendar_year, calendar_quarter_desc,
calendar_month_desc, t.time_id) gid_t,
SUM(amount_sold) s_sold, COUNT(amount_sold) c_sold, COUNT(*) cnt
FROM SALES s, TIMES t, PRODUCTS p
WHERE s.time_id = t.time_id AND
p.prod_name IN ('Bounce', 'Y Box') AND s.prod_id = p.prod_id
GROUP BY
ROLLUP(calendar_year, calendar_quarter_desc, calendar_month_desc, t.time_id),
ROLLUP(prod_category, prod_subcategory, p.prod_id);

手順4   比較問合せの作成

ここまでで比較問合せの段階が設定されました。すべての時間レベルでの周期ごとの比較計算を取得できます。それには、時間ディメンションに沿った稠密なデータで、分析関数を階層的キューブに適用する必要があります。

次に、各時間レベルで実行可能な計算の一部を示します。

- すべての時間レベルでの前期の売上の合計

- 前の期間からの売上の変化

- すべての時間レベルでの1年前の同じ期間の売上の合計

- 昨年の同じ期間からの売上の変化

次の例では、この4つの計算をすべて実行します。ビューcube_prod_timeおよびedge_timeのパーティション外部結合を使用して、dense_cube_prod_timeという稠密なデータのインライン・ビューを作成します。続いて、前の単一レベルの例と同様に、問合せでLAG関数を使用します。外部WHERE句は、2001年8月の各日、8月全体、2001年の第3四半期の3つのレベルで時間を指定します。結果の最後の2行には、月レベルと四半期レベルの集計が含まれます。SQL*Plusを使用している場合は、次のコマンドで列ヘッダーを調整すると、結果が読みやすくなります。このコマンドを実行すると、列ヘッダーが折り返されて行が短くなります。

col sales_prior_period heading 'sales_prior|_period'
col variance_prior_period heading 'variance|_prior|_period'
col sales_same_period_prior_year heading 'sales_same|_period_prior|_year'
col variance_same_period_p_year heading 'variance|_same_period|_prior_year'

次に、現在の売上を前期の売上および1年前の売上と比較する問合せを示します。

SELECT SUBSTR(prod,1,4) prod, SUBSTR(Hierarchical_Time,1,12) ht,
sales, sales_prior_period,
sales - sales_prior_period variance_prior_period,
sales_same_period_prior_year,
sales - sales_same_period_prior_year variance_same_period_p_year
FROM
(SELECT cat, subcat, prod, gid_p, gid_t,
Hierarchical_Time, yr, qtr, mon, day, sales,
LAG(sales, 1) OVER (PARTITION BY gid_p, cat, subcat, prod,
gid_t ORDER BY yr, qtr, mon, day)
sales_prior_period,
LAG(sales, 1) OVER (PARTITION BY gid_p, cat, subcat, prod,
gid_t, qtr_num, mon_num, day_num ORDER BY yr)
sales_same_period_prior_year
FROM
(SELECT c.gid, c.cat, c.subcat, c.prod, c.gid_p,
t.gid_t, t.yr, t.qtr, t.qtr_num, t.mon, t.mon_num,
t.day, t.day_num, t.Hierarchical_Time, NVL(s_sold,0) sales
FROM cube_prod_time c
PARTITION BY (gid_p, cat, subcat, prod)
RIGHT OUTER JOIN edge_time t
ON ( c.gid_t = t.gid_t AND
c.Hierarchical_Time = t.Hierarchical_Time)
) dense_cube_prod_time
)                        --side by side current and prior year sales
WHERE prod IN (139) AND gid_p=0 AND    --1 product and product level data
( (mon IN ('2001-08' ) AND gid_t IN (0, 1)) OR --day and month data
(qtr IN ('2001-03' ) AND gid_t IN (3)))        --quarter level data
ORDER BY day;

variance    sales_same     variance
sales_prior     _prior _period_prior _same_period
PROD HT                SALES     _period    _period         _year  _prior_year
---- ------------ ---------- ----------- ---------- ------------- ------------
139  01-AUG-01_3           0           0          0             0            0
139  02-AUG-01_3     1347.53           0    1347.53             0      1347.53
139  03-AUG-01_3           0     1347.53   -1347.53         42.36       -42.36
139  04-AUG-01_3       57.83           0      57.83        995.75      -937.92
139  05-AUG-01_3           0       57.83     -57.83             0            0
139  06-AUG-01_3           0           0          0             0            0
139  07-AUG-01_3      134.81           0     134.81        880.27      -745.46
139  08-AUG-01_3     1289.89      134.81    1155.08             0      1289.89
139  09-AUG-01_3           0     1289.89   -1289.89             0            0
139  10-AUG-01_3           0           0          0             0            0
139  11-AUG-01_3           0           0          0             0            0
139  12-AUG-01_3           0           0          0             0            0
139  13-AUG-01_3           0           0          0             0            0
139  14-AUG-01_3           0           0          0             0            0
139  15-AUG-01_3       38.49           0      38.49       1104.55     -1066.06
139  16-AUG-01_3           0       38.49     -38.49             0            0
139  17-AUG-01_3       77.17           0      77.17       1052.03      -974.86
139  18-AUG-01_3     2467.54       77.17    2390.37             0      2467.54
139  19-AUG-01_3           0     2467.54   -2467.54        127.08      -127.08
139  20-AUG-01_3           0           0          0             0            0
139  21-AUG-01_3           0           0          0             0            0
139  22-AUG-01_3           0           0          0             0            0
139  23-AUG-01_3     1371.43           0    1371.43             0      1371.43
139  24-AUG-01_3      153.96     1371.43   -1217.47        2091.3     -1937.34
139  25-AUG-01_3           0      153.96    -153.96             0            0
139  26-AUG-01_3           0           0          0             0            0
139  27-AUG-01_3     1235.48           0    1235.48             0      1235.48
139  28-AUG-01_3       173.3     1235.48   -1062.18       2075.64     -1902.34
139  29-AUG-01_3           0       173.3     -173.3             0            0
139  30-AUG-01_3           0           0          0             0            0
139  31-AUG-01_3           0           0          0             0            0
139  2001-08_2       8347.43     7213.21    1134.22       8368.98       -21.55
139  2001-03_1       24356.8    28862.14   -4505.34      24168.99       187.81

最初のLAG関数(sales_prior_period)により、gid_p、cat、subcat、prodおよびgid_tのデータがパーティション化され、すべての時間ディメンション列で行が順序付けされます。この関数ではオフセット1を渡すことで前期の売上値が取得されます。2番目のLAG関数(sales_same_period_prior_year)では、この他にqtr_num、mon_numおよびday_numの各列でもデータがパーティション化され、yrで順序付けされます。その結果、オフセット1によって1年前の同じ期間の売上が計算されます。最も外側のSELECT句は、分散を計算します。

ディメンションのカスタム・メンバーの作成: 例

多くの分析SQLタスクでは、ディメンションでカスタム・メンバーを定義すると役立ちます。たとえば、分析用に特定の期間を定義するとします。パーティション外部結合を使用して、メンバーを一時的にディメンションに追加できます。新しくSQLに導入されたMODEL句は、ディメンション内の新しいメンバーが関与する複雑なシナリオの作成に適しています。詳細は、第23章「モデリングのSQL」を参照してください。

タスクの例として、timeディメンションの新しいメンバーを定義する場合はどうするかを示します。ここでは、timeディメンションで月レベルの13番目のメンバーを作成します。この13番目の月は、2001年の各四半期の最初の月における各製品の売上の合計として定義します。

これを行うには、2つの手順があります。ここでは、前の例で作成したビューと表を使用します。この2つの手順は必須です。まず、適切なディメンションに追加される新しいメンバーを持つビューを作成します。ビューでは、UNION ALL演算子を使用して新しいメンバーが追加されます。カスタム・メンバーを使用して問合せを実行するには、CASE式およびパーティション外部結合を使用します。

timeディメンションの新しいメンバーは、次のビューで作成されます。

CREATE OR REPLACE VIEW time_c AS
(SELECT * FROM edge_time
UNION ALL
SELECT '2001-13_2', 2001, 5, '2001-05', 13, '2001-13', null, null,
8 -- <gid_of_mon>
FROM DUAL);

この文では、(前の例で定義した)edge_timeビューとユーザー定義の13番目の月のUNION ALLを実行することで、ビューtime_cが定義されます。標準メンバーからカスタム・メンバーを区別するために、gid_t値として8が選択されています。UNION ALLでは、DUAL表からSELECTを実行することで、13番目の月のメンバーの属性が指定されます。グルーピングid(列gid_t)は8に設定され、四半期の番号は5に設定されます。

したがって、2番目の手順は、問合せでインライン・ビューを使用して、cube_prod_timeとtime_cのパーティション外部結合を実行することです。この手順では、製品集計の各レベルで13番目の月の売上データを作成します。メインの問合せでは、分析関数SUMがCASE式とともに使用され、各四半期の最初の月における売上合計として定義された13番目の月が計算されます。

SELECT * FROM  (SELECT SUBSTR(cat,1,12) cat, SUBSTR(subcat,1,12) subcat,
prod, mon, mon_num,
SUM(CASE WHEN mon_num IN (1, 4, 7, 10)
THEN s_sold
ELSE NULL
END)
OVER (PARTITION BY gid_p, prod, subcat, cat, yr) sales_month_13
FROM
(SELECT c.gid, c.prod, c.subcat, c.cat, gid_p,
t.gid_t, t.day, t.mon, t.mon_num,
t.qtr, t.yr, NVL(s_sold,0) s_sold
FROM cube_prod_time c
PARTITION BY (gid_p, prod, subcat, cat)
RIGHT OUTER JOIN time_c t
ON (c.gid_t = t.gid_t AND
c.Hierarchical_Time = t.Hierarchical_Time)
)
)
WHERE mon_num=13;

CAT          SUBCAT             PROD MON         MON_NUM SALES_MONTH_13
------------ ------------ ---------- -------- ---------- --------------
Electronics  Game Console         16 2001-13          13      762334.34
Electronics  Y Box Games         139 2001-13          13       75650.22
Electronics  Game Console            2001-13          13      762334.34
Electronics  Y Box Games             2001-13          13       75650.22
Electronics                          2001-13          13      837984.56
2001-13          13      837984.56

SUM関数では、CASEが使用され、データが各年の1、4、7、10月に制限されます。データセットが2製品のみと小さいため、結果のロールアップ値は、必然的により低いレベルの集計の繰り返しになります。ロールアップ値のより現実的なセットでは、Game ConsoleおよびY Box Gamesサブカテゴリからより多くの製品を、基底のマテリアライズド・ビューに含めることができます。

その他の分析およびレポートの機能

この項では、次に示す追加の分析機能について説明します。

- WIDTH_BUCKET関数

- 線形代数

- CASE式

- 高頻度項目セット

WIDTH_BUCKET関数

WIDTH_BUCKET関数は、特定の式について、この式の結果に対して評価後に割り当てられるバケット番号を戻します。この関数を使用すると、ヒストグラムを生成できます。ヒストグラムでは、データ集合はインターバル・サイズ(最大値から最小値まで)が等しい等幅バケットに分割されます。各バケットに保持される行数は変動します。関連する関数NTILEでは、等度数バケットが作成されます。

ヒストグラムを生成できるのは、数値または日付のデータ型の場合のみです。したがって、最初の3つのパラメータは、すべて数値型またはすべて日付型の式にする必要があります。他の型の式は使用できません。最初のパラメータがNULLの場合、結果はNULLです。2番目または3番目のパラメータがNULLの場合、NULL値は日付または数値のディメンションの範囲について終了点(または点)を示すことができないため、エラー・メッセージが戻されます。最後のパラメータ(バケット数)は、正の整数値に評価される数値型の式にする必要があります。0(ゼロ)、NULLまたは負の値の場合はエラーになります。

バケットには、0から(n+1)の番号が付いています。バケット0には最小値未満の値のカウントが保持されます。バケット(n+1)には、指定した最大値以上の値のカウントが保持されます。

WIDTH_BUCKETの構文

WIDTH_BUCKETは、パラメータとして4つの式をとります。最初のパラメータは、ヒストグラムの対象となる式です。2番目と3番目のパラメータは、最初のパラメータについて許容範囲の端点を示す式です。4番目のパラメータは、バケット数を示します。

WIDTH_BUCKET(expression, minval expression, maxval expression, num buckets)

表customersからの次のデータを考えてみます。これは、17人の顧客の与信限度額を示しています。このデータは、例22-24の問合せで収集されます。

CUST_ID    CUST_CREDIT_LIMIT
---------  -----------------
10346               7000
35266               7000
41496              15000
35225              11000
3424               9000
28344               1500
31112               7000
8962               1500
15192               3000
21380               5000
36651               1500
30420               5000
8270               3000
17268              11000
14459              11000
13808               5000
32497               1500
100977               9000
102077               3000
103066              10000
101784               5000
100421              11000
102343               3000

このcustomers表には、cust_credit_limit列に1500から15000までの値があり、WIDTH_BUCKET (cust_credit_limit, 0, 20000, 4)を使用すると、これらの値を1から4の番号が付いた4つの等幅バケットに割り当てることができます。理想的には、各バケットは実際の数値直線のクローズ/オープン・インターバルです。たとえば、バケット番号2は5000.0000から9999.9999...のスコアに割り当てられており、[5000, 10000)は5,000がインターバルに含まれ、10,000は除外されることを示す場合があります。範囲[0, 20,000)外の他の値に対処するために、0未満の値は番号0で指定されたアンダーフロー・バケットに割り当てられ、20,000以上の値は番号5(通常はバケット数+1)で指定されたオーバーフロー・バケットに割り当てられます。バケットの割当て方法については、図221-3を参照してください。

図22-3 バケットの割当て

バケットの境界は、WIDTH_BUCKET (cust_credit_limit, 20000, 0, 4)のように逆順で指定することもできます。境界が逆順になっている場合、バケットはオープン/クローズ・インターバルとなります。この例で、バケット番号1は(15000,20000]、バケット番号2は(10000,15000]、バケット番号4は(0,5000]となります。オーバーフロー・バケットの番号は0 (20000, +infinity)、アンダーフロー・バケットの番号は5 (-infinity, 0)です。

バケット・カウント・パラメータが0または負の値の場合は、エラーになります。

例22-24 WIDTH_BUCKET

次の問合せは、customers表の与信限度額のバケット番号を示しています。境界は、それぞれ通常の順序および逆順で指定されています。範囲は0から20,000です。

SELECT cust_id, cust_credit_limit,
WIDTH_BUCKET(cust_credit_limit,0,20000,4) AS WIDTH_BUCKET_UP,
WIDTH_BUCKET(cust_credit_limit,20000, 0, 4) AS WIDTH_BUCKET_DOWN
FROM customers WHERE cust_city = 'Marshal';

CUST_ID CUST_CREDIT_LIMIT WIDTH_BUCKET_UP WIDTH_BUCKET_DOWN
---------- ----------------- --------------- -----------------
10346              7000               2                 3
35266              7000               2                 3
41496             15000               4                 2
35225             11000               3                 2
3424              9000               2                 3
28344              1500               1                 4
31112              7000               2                 3
8962              1500               1                 4
15192              3000               1                 4
21380              5000               2                 4
36651              1500               1                 4
30420              5000               2                 4
8270              3000               1                 4
17268             11000               3                 2
14459             11000               3                 2
13808              5000               2                 4
32497              1500               1                 4
100977              9000               2                 3
102077              3000               1                 4
103066             10000               3                 3
101784              5000               2                 4
100421             11000               3                 2
102343              3000               1                 4

線形代数

線形代数は数学の一分野であり、実践的にも幅広く応用されています。線形代数を使用して表現できる事柄を研究や分析の対象として含む領域は多く、その一部として例をあげれば、統計学(多重線形回帰分析や主成分分析)、データ・マイニング(クラスタリングや分類)、バイオインフォマティクス(マイクロアレイ・データの分析)、オペレーションズ・リサーチ(サプライ・チェーンなどの最適化問題)、経済学(消費者需要データの分析)、金融(資産分配問題)など様々です。無償で利用できる線形代数用ライブラリも、様々な種類のものが提供されています。OracleのUTL_NLAパッケージでは、強力な線形代数ライブラリとして定評のあるBLASとLAPACKのためのPL/SQL行列データ型やPL/SQLラッパー・サブプログラムを利用できます。

線形代数の土台となっているのは行列演算です。これまで、PL/SQLで行列演算を実行するためには、PL/SQLのネイティブ・データ型を基にした行列の表現を考案した上で、行列演算のルーチンを一から記述する必要がありました。そのためには、きわめて手間のかかるプログラミングが必要となり、それを実装した後のパフォーマンスにも限界がありました。一方、開発者が独自のルーチンを作成せず、データを外部パッケージに送って処理しようとすると、データのやりとりに時間がかかる場合があります。UTL_NLAパッケージを使用すれば、データをOracle内部で処理できるのみでなく、手間のかかるプログラミングも不要になり、高速な実装が実現されます。

例22-25 線形代数

ビジネス分析にOracleの線形代数サポートをどのように活用するかについて、1つの例を紹介します。UTL_NLAパッケージを使用して作成された多重線形回帰アプリケーションを起動します。この多重線形回帰アプリケーションは、OLS_Regressionというオブジェクトに実装されます。OLS Regressionオブジェクトのサンプル・ファイルは、$ORACLE_HOME/plsql/demoにあります。

小売業者がそのマーケティング・プログラムの有効性を分析する場合を考えます。店舗ごとにそのマーケティング費用が、メディア広告(media)、販促活動(promo)、割引券(disct)およびダイレクト・メール(dmail)の各プログラムに割り当てられます。回帰分析を行って、平均的な店舗におけるある年の売上高(sales)と4つのマーケティング・プログラムにかかる費用との間に線形関係を見出します。マーケティング・データが次の表に格納されているとします。

sales_marketing_data (
/* Store information*/
store_no   NUMBER,
year       NUMBER,
/* Sales revenue (in dollars)*/
sales      NUMBER,   /* sales amount*/
/* Marketing expenses (in dollars)*/
media      NUMBER,   /*media advertisements*/
promo      NUMBER,   /*promotions*/
disct      NUMBER,   /*dicount coupons*/
dmail      NUMBER,   /*direct mailers*/

ここで係数を基にして、次のような売上高とマーケティングの線形モデルを作成できます。

Sales Revenue = a  + b Media Advisements
+ c Promotions
+ d Discount Coupons
+ e Direct Mailer

このモデルは、OLS Regressionオブジェクトを参照する次のようなビューとして実装できます。

CREATE OR REPLACE VIEW sales_marketing_model (year, ols)
AS SELECT year,
OLS_Regression(
/* mean_y => */
AVG(sales),
/* variance_y => */
var_pop(sales),
/* MV mean vector => */
UTL_NLA_ARRAY_DBL (AVG(media),AVG(promo),
AVG(disct),AVG(dmail)),
/* VCM variance covariance matrix => */
UTL_NLA_ARRAY_DBL (var_pop(media),covar_pop(media,promo),
covar_pop(media,disct),covar_pop(media,dmail),
var_pop(promo),covar_pop(promo,disct),
covar_pop(promo,dmail),var_pop(disct),
covar_pop(disct,dmail),var_pop(dmail)),
/* CV covariance vector => */
UTL_NLA_ARRAY_DBL (covar_pop(sales,media),covar_pop(sales,promo),
covar_pop(sales,disct),covar_pop(sales,dmail)))
FROM sales_marketing_data
GROUP BY year;

マーケティング・プログラム・マネージャはこのビューを使用することで、「売上高とマーケティングに関するこのモデルは2004年のデータに適合しているか、つまり、この重相関はある許容値(たとえば0.9)よりも大きいか」などの分析を実行できます。では、相関が複数の場合は、許容値(0.9など)よりも大きくなりますか。これに対応する問合せをSQLで記述するとすれば、次のようになります。

SELECT model.ols.getCorrelation(1)
AS "Applicability of Linear Model"
FROM sales_marketing_model model
WHERE year = 2004;

また、「2003年にマーケティング・プログラムをまったく実施しなかった店舗の予想基準売上高はいくらか」や、「2004年のマーケティング・プログラムではどの要素が最も効果的であったか、つまり、費用増分に対する売上高増分の割合が最大であったプログラムはどれか」などの問題を解決することもできます。

UTL_NLAパッケージおよび線形代数の使用方法の詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

CASE式

Oracleでは、現在、単純および検索CASE文をサポートしています。CASE文は、目的がDECODE文に似ていますが、DECODE文以上の柔軟性および機能性が提供されます。従来のDECODE文より簡単に理解でき、パフォーマンスも向上します。CASE文は、一般に、カテゴリを年齢などのバケット(たとえば20から29、30から39など)に分割する場合に使用します。

単純CASE文の構文は、次のとおりです。

CASE expr WHEN comparison_expr THEN return_expr
[, WHEN comparison_expr THEN return_expr]... [ELSE else_expr] END

単純CASE式は、expr値がcomparison_exprと等価かどうかを調べます。

検索CASE文の構文は、次のとおりです。

CASE WHEN condition THEN return_expr [, WHEN condition THEN return_expr]
... ELSE else_expr] END

検索CASE式では、等価性のみではなく、あらゆる種類の条件を指定できます。

指定できる引数の最大数は65,535です。WHEN ... THENの各組は、2つの引数として数えられます。この制限を超えないようにするには、return_expr自体がCASE式になるようにCASE式をネストします。

例22-26 CASE

ある会社のすべての従業員の平均給与を検索するとします。従業員の給与が2000ドル未満の場合、問合せにはかわりに2000ドルを使用します。CASE文を使用しない場合、この問合せは次のようになります。

SELECT AVG(foo(e.salary)) FROM employees e;

これは、hrサンプル・スキーマに対して実行する問合せです。この場合のfooは、入力が2001以上の場合はその入力値を、それ以外の場合は2000を戻す関数です。この問合せは、各行で関数を起動する必要があるため、パフォーマンスを考慮する必要があります。また、独自関数を記述すると、開発の負荷も大きくなることがあります。

データベースでPL/SQLを使用せずにCASE式を使用すると、この問合せは次のように記述できます。

SELECT AVG(CASE when e.salary > 2000 THEN e.salary ELSE 2000 end)
AS avg_sal_2k_floor
FROM employees e;

CASE式を使用すると、独自関数を開発する必要がなく、高速で実行できます。

例22-27 独立したサブセットを集計するCASE

データの複数のサブセットに対して集計を実行する際、通常のGROUP BYで対応できない場合は、CASEを集計関数の内部で使用できます。たとえば、前述の例において、それぞれ独自のCASE式を持つ複数のAVG列をSELECT構文のリストに含めることができます。このようにすると、給与が0から2000または2000から5000の範囲にある全従業員の平均給与を求める次のような問合せを作成できます。次のようになります。

SELECT AVG(CASE WHEN e.sal BETWEEN 0 AND 2000 THEN e.sal ELSE null END) avg2000,
AVG(CASE WHEN e.sal BETWEEN 2001 AND 5000 THEN e.sal ELSE null END) avg5000
FROM emps e;

この問合せでは、独立したサブセット・データの集計結果を別々の列に表示していますが、CASE式をGROUP BY句に追加すると、集計結果を単一の列の複数の行に表示できます。次の項では、CASEを使用してヒストグラムを作成する2つのアプローチとともに、このアプローチの柔軟性を示します。

ヒストグラムの作成

ユーザー定義バケット(バケット数および各バケットの幅の両方)を含むヒストグラムを作成する場合は、CASE文を使用します。次に、CASE文で作成されたヒストグラムの例を2つ示します。最初の例では、ヒストグラムの合計が複数の列に示され、単一の行が戻されます。2番目の例では、ヒストグラムはラベル列および単一の合計列で示され、複数の行が戻されます。

例22-28 ヒストグラムの例1

SELECT SUM(CASE WHEN cust_credit_limit BETWEEN  0 AND 3999 THEN 1 ELSE 0 END)
AS "0-3999",
SUM(CASE WHEN cust_credit_limit BETWEEN  4000 AND 7999 THEN 1 ELSE 0 END)
AS "4000-7999",
SUM(CASE WHEN cust_credit_limit BETWEEN  8000 AND 11999 THEN 1 ELSE 0 END)
AS "8000-11999",
SUM(CASE WHEN cust_credit_limit BETWEEN  12000 AND 16000 THEN 1 ELSE 0 END)
AS "12000-16000"
FROM customers WHERE cust_city = 'Marshal';

0-3999  4000-7999 8000-11999 12000-16000
---------- ---------- ---------- -----------
8          7          7           1

例22-29 ヒストグラムの例2

SELECT (CASE WHEN cust_credit_limit BETWEEN  0 AND 3999 THEN  ' 0 - 3999'
WHEN cust_credit_limit BETWEEN  4000 AND 7999 THEN ' 4000 - 7999'
WHEN cust_credit_limit BETWEEN  8000 AND 11999 THEN  ' 8000 - 11999'
WHEN cust_credit_limit BETWEEN  12000 AND 16000 THEN '12000 - 16000' END)
AS BUCKET, COUNT(*) AS Count_in_Group
FROM customers WHERE cust_city = 'Marshal' GROUP BY
(CASE WHEN cust_credit_limit BETWEEN  0 AND 3999 THEN ' 0 - 3999'
WHEN cust_credit_limit BETWEEN  4000 AND 7999 THEN ' 4000 - 7999'
WHEN cust_credit_limit BETWEEN  8000 AND 11999 THEN  ' 8000 - 11999'
WHEN cust_credit_limit BETWEEN  12000 AND 16000 THEN '12000 - 16000' END);

BUCKET        COUNT_IN_GROUP
------------- --------------
0 - 3999                  8
4000 - 7999               7
8000 - 11999              7
12000 - 16000              1

高頻度項目セット

一定のイベントがどのくらいの頻度で発生するか(たとえば、ある顧客が食料品店でどのくらいの頻度でミルクを購入するか)をカウントするかわりに、複数のイベントがどのくらいの頻度で同時に発生するか(たとえば、ある顧客が食料品店でどのくらいの頻度でミルクとシリアルを同時に購入するか)をカウントできます。こうした複数のイベントをカウントするには、高頻度項目セット(名前のとおり複数項目のセット)というものを使用します。項目セットの例としては、特定の顧客が食料品店での1回の買い物で購入したすべての製品(通常はマーケット・バスケットと呼ばれる)、1回のセッションでユーザーがアクセスしたWebページ、特定の顧客が利用する金融サービスなどがあります。

高頻度項目セットを使用する実際の目的は、最も頻繁に発生する項目セットを見つけることにあります。食料品店のPOSデータを分析する場合であれば、たとえば、同時に購入される項目の組として最も多いものはミルクとバナナであるということがわかるわけです。このため、高頻度項目セットは長い間、小売業界のビジネス・インテリジェンス環境で最も一般的なマーケット・バスケット分析用ツールとして使用されてきました。高頻度項目セットの計算機能はデータベースに統合されています。操作はリレーショナル表の上で行われ、SQLを介してアクセスします。データベースと統合することで、次のような大きな利点があります。

- これまで高頻度項目セット操作に依存していたアプリケーションの場合は、より単純な実装が可能になり、パフォーマンスが大幅に向上します。

- これまで高頻度項目セットを使用していなかったSQLベース・アプリケーションの場合は、簡単に拡張してこの機能を利用できるようになります。

高頻度項目セットの分析は、PL/SQLパッケージDBMS_FREQUENT_ITEMSETSで実行されます。詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。また、高頻度項目セットの使用例は「高頻度項目セット」を参照してください。

23 モデリングのSQL

この章では、SQLモデリングの使用方法について説明します。次の内容が含まれます。

- SQLモデリングの概要

- SQLモデリングの基本的なトピック

- SQLモデリングの高度なトピック

- SQLモデリングのパフォーマンスに関する考慮事項

- SQLモデリングの例

SQLモデリングの概要

MODEL句によって、SQL計算の機能性と柔軟性が向上します。MODEL句を使用すると、問合せ結果から多次元配列を作成し、この配列に(ルールと呼ばれる)式を適用して新しい値を計算できます。ルールは、基本的な算術式から再帰型を使用した連立方程式まで様々です。一部のアプリケーションでは、MODEL句の機能をPCベースのスプレッドシートと置き換えることができます。SQLのモデルには、Oracle Databaseが持つスケーラビリティ、管理性、コラボレーション機能、セキュリティ機能などの長所が生かされています。問合せのコア・エンジンは、無制限の量のデータを処理できます。データベース内でモデルを定義および実行することにより、別個のモデリング環境間で大規模なデータセットを転送する必要がなくなります。モデルはワークグループ間で簡単に共有できるため、すべてのアプリケーションで計算の一貫性を維持できます。モデルの共有と同様に、アクセスもOracleのセキュリティ機能を使用して厳密に制御できます。豊富な機能を備えたMODEL句を使用することで、あらゆるタイプのアプリケーションを強化できます。

MODEL句を使用すると、パーティション列、ディメンション列およびメジャー列の3つのグループに問合せの列を対応付けることによって、多次元配列を作成できます。これらの要素は、次のタスクを実行します。

- パーティション列では、第22章「分析計算およびレポート用SQL関数」で説明した分析関数のパーティションと同様の方法で結果セットの論理ブロックを定義します。MODEL句のルールは、他のパーティションとは別個にパーティションごとに適用されます。したがって、パーティションは、MODEL句での計算をパラレル化するための境界点となります。

- ディメンション列では、多次元配列を定義します。この列は、パーティション内のセルの識別に使用します。デフォルトでは、ディメンションの1つの完全な組合せはパーティション内の1つのセルのみを識別する必要があります。デフォルト・モードの場合、リレーショナル表のキーと同様のものとみなされます。

- メジャーは、スター・スキーマのファクト表のメジャーと等価です。通常、メジャーには販売単位やコストなどの数値が格納されています。各セルにアクセスするには、各セルのディメンションの完全な組合せを指定します。各パーティションには、特定のディメンションの組合せと一致するセルが含まれることがあります。

MODEL句ではルールを指定して、パーティション列およびディメンション列で定義された多次元配列内のセルのメジャー値を操作できます。ルールは、ディメンション値を直接指定して、メジャー列値へのアクセスおよび更新を行います。ルールで参照を使用することにより、モデルの可読性が向上します。ルールは簡潔かつ柔軟なもので、表現力を最大限に高めるためにワイルド・カードおよびループ・コンストラクトを使用できます。Oracle Databaseでは、効率的にルールを評価し、可能なかぎりモデル計算をパラレル化し、MODEL句を他のSQL句とシームレスに統合します。したがって、データベースのビジネス・モデルを計算する上でMODEL句はスケーラブルで管理性に優れた方法であるといえます。

図23-1に、SQLのモデリング機能の概要を示します。図は3つの部分から構成されています。最上部では、一般的な表をパーティション列、ディメンション列およびメジャー列に分割する概念を示しています。中央の部分では、2002年のProd1およびProd2の値を計算する2つのルールを示しています。最下部では、仮想データを含む表にルールを適用した問合せの出力を示しています。白の部分の出力はデータベースから取得した元のデータを表し、グレーの部分の出力はルールで計算された行を表します。パーティションAの結果はパーティションBの結果とは独立して計算されていることに注目してください。

図23-1 モデルの要素

SQL Modelでのデータの処理方法

図23-2に、単純なMODEL句における処理フローを示します。ここでは、3つのルールを含んだMODEL句を通してデータの処理を理解していきます。ルールの1つでは既存の値を更新し、残りの2つのルールでは予測用に新しい値を作成します。図では、問合せで取得したデータの行がMODEL句に入力され、配列に再配置されていることが示されています。配列が定義されると、ルールが1つずつデータに適用されます。図23-2のグレーのセルはルールによって作成された新しいデータを表し、楕円で囲まれたセルは新しい値のソース・データを表しています。最後に、更新された値と新規に作成された値の両方を含むデータが行形式に再配置され、問合せの結果として表されます。この問合せでは、表へのデータの挿入はまったく行われていないことに注目してください。

図23-2 MODELの処理フロー

SQLモデリングを使用する理由

Oracleのモデリングでは、高度な計算をデータに対して実行できます。通常は、ビジネス・ルールをデータに適用してレポートを生成する場合などに使用します。Oracle Databaseでは、モデリングの計算をデータベースに統合しているため、パフォーマンスと管理性が飛躍的に向上します。次の問合せを考えてみます。

SELECT SUBSTR(country, 1, 20) country,
SUBSTR(product, 1, 15) product, year, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan')
MODEL
PARTITION BY (country) DIMENSION BY (product, year)
MEASURES (sales sales)
RULES
(sales['Bounce', 2002] = sales['Bounce', 2001] + sales['Bounce', 2000],
sales['Y Box', 2002] = sales['Y Box', 2001],
sales['All_Products', 2002] = sales['Bounce', 2002] + sales['Y Box', 2002])
ORDER BY country, product, year;

この問合せでは、「ベース・スキーマ」に示すように、sales_viewのデータをcountryでパーティション化して、3つのルールで定義されたモデル計算を国ごとに実行します。このモデルでは、2002年のBounceの売上を2000年と2001年の売上の合計として計算し、2002年のY Boxの売上を2001年の売上と同じ値に設定します。また、2002年については、同年のBounceとY Boxの売上の合計となる、新製品カテゴリAll_Productsを導入しています(sales_viewには、製品All_Productsはありません)。この問合せの出力を次に示します。この中で太字は新しい値を示します。

COUNTRY              PRODUCT               YEAR      SALES
-------------------- --------------- ---------- ----------
Italy                Bounce                1999    2474.78
Italy                Bounce                2000    4333.69
Italy                Bounce                2001     4846.3
Italy                Bounce                2002    9179.99
...
Italy                Y Box                 1999   15215.16
Italy                Y Box                 2000   29322.89
Italy                Y Box                 2001   81207.55
Italy                Y Box                 2002   81207.55
...
Italy                All_Products          2002   90387.54
...
Japan                Bounce                1999     2961.3
Japan                Bounce                2000    5133.53
Japan                Bounce                2001     6303.6
Japan                Bounce                2002   11437.13
...
Japan                Y Box                 1999   22161.91
Japan                Y Box                 2000   45690.66
Japan                Y Box                 2001   89634.83
Japan                Y Box                 2002   89634.83
...
Japan                All_Products          2002  101071.96
...

BounceとY Boxの売上値は入力データ内に存在するのに対し、All_Productsの値は導出されていることに注目してください。

SQLモデリングの機能

Oracle DatabaseのMODEL句の機能を次に示します。

- ディメンション値を使用したセルのアドレッシング
個々の行のメジャー列は、多次元配列のセルのように処理され、ディメンション値を使用して参照および更新できます。たとえば、ファクト表ft(country, year, sales)では、countryおよびyearをディメンション列に、salesをメジャーに指定し、特定の国および年の売上をsales[country='Spain', year=1999]として参照できます。この場合、1999年のSpainの売上値を取得できます。また、同じ内容を短縮形式で表したsales['Spain', 1999]を使用することもできます。ただし、両者の表記には、わずかな意味の違いがあります。詳細は、「セル参照」を参照してください。

- シンボリック配列の計算
データの処理に、ルールと呼ばれる一連の式を指定できます。ルールでは、個々のセル、セルの集合または範囲で関数を呼び出すことができます。個々のセルを対象にした例を次に示します。

sales[country='Spain',year=2001] = sales['Spain',2000]+ sales['Spain',1999]

この場合、2001年のSpainの売上は、1999年と2000年におけるSpainの売上の合計に設定されています。次に、セルの範囲を対象とした例を示します。

sales[country='Spain',year=2001] =
MAX(sales)['Spain',year BETWEEN 1997 AND 2000]

この場合、2001年のSpainの売上は、1997年から2000年のSpainにおける最高の売上と等しくなるよう設定されています。

- UPSERT、UPSERT ALLおよびUPDATEオプション
UPSERTオプション(デフォルト)を使用すると、入力データに存在しないセルの値を作成できます。参照先のセルがデータに存在する場合、セルが更新されます。参照先のセルがデータに存在せず、なおかつルールが適切な表記法を使用している場合は、セルが挿入されます。UPSERT ALLオプションでは、より幅広い種類のルールに対してUPSERTの動作を適用できます。反対に、UPDATEオプションでは、新しいセルは一切挿入されません。

これらのオプションは、グローバルに指定(すべてのルールに適用)することも、ルールごとに指定することもできます。ルール・レベルでオプションを指定すると、グローバル・オプションはオーバーライドされます。次のルールを考えてみます。

UPDATE sales['Spain', 1999] = 3567.99,
UPSERT sales['Spain', 2001] = sales['Spain', 2000]+ sales['Spain', 1999]

最初のルールでは、1999年のSpainにおける売上のセルを更新します。2番目のルールでは、2001年のSpainにおける売上のセルが存在する場合は更新し、存在しない場合は新しいセルを作成します。

- ディメンションのワイルド・カード指定
ANYおよびIS ANYを使用すると、ディメンションの値をすべて指定できます。たとえば、次の文があるとします。

sales[ANY, 2001] = sales['Japan', 2000]

このルールでは、2001年のすべての国の売上を2000年のJapanの売上値と同じに設定しています。この場合、ディメンションの値はNULLも含め、すべてANYの指定を満たしています。次のように、IS ANY述語を使用して同じ処理を指定することもできます。

sales[country IS ANY, 2001] = sales['Japan', 2000]

- CV関数を使用したディメンション値へのアクセス
ルールの右辺でCV関数を使用すると、ルールの左辺で参照されるセルのディメンション列の値にアクセスできます。これによって、類似した計算を実行する複数のルールを単一のルールに結合できるため、指定が簡潔になります。たとえば、次のルールを結合できます。

sales[country='Spain', year=2002] = 1.2 * sales['Spain', 2001],
sales[country='Italy', year=2002] = 1.2 * sales['Italy', 2001],
sales[country='Japan', year=2002] = 1.2 * sales['Japan', 2001]

これらは、次の単一のルールに結合できます。

sales[country IN ('Spain', 'Italy', 'Japan'), year=2002] = 1.2 *
sales[CV(country), 2001]

この場合、CV関数は、左辺のcountryディメンションの値をルールの右辺に渡します。

- 順序付け計算
セルの集合を更新するルールでは、結果はディメンション値の順序に依存する場合があります。ルールでORDER BYを指定すると、ディメンション値を特定の順序に強制できます。たとえば、次のルールを考えてみます。

sales[country IS ANY, year BETWEEN 2000 AND 2003] ORDER BY year =
1.05 * sales[CV(country), CV(year)-1]

これによって、yearは時系列的に昇順で参照されます。

- ルールの自動順序付け
AUTOMATIC ORDERキーワードを使用すると、セル間の依存関係に基づいてMODEL句のルールを自動的に順序付けできます。たとえば、次の割当ての場合、2番目と3番目のルールが最初のルールよりも先に処理されます。これは、最初のルールが最後の2つのルールに依存しているためです。

RULES AUTOMATIC ORDER
{sales[c='Spain', y=2001] = sales[c='Spain', y=2000]
+ sales[c='Spain', y=1999]
sales[c='Spain', y=2000] = 50000,
sales[c='Spain', y=1999] = 40000}

- 反復ルール評価
反復ルール評価を指定すると、終了条件が満たされるまで、反復的にルールが評価されます。次の指定を考えてみます。

MODEL DIMENSION BY (x) MEASURES (s)
RULES ITERATE (4) (s[x=1] = s[x=1]/2)

この文では、式s[x=1] = s[x=1]/2の評価を4回繰り返すように指定しています。反復回数は、MODEL句のITERATEオプションで指定しています。また、UNTIL句を使用すると、終了条件も指定できます。

反復ルール評価は、ビジネス・アプリケーションのエンティティ間の再帰的関係をモデル化する場合に重要な機能です。たとえば、ローンの総額が金利によって異なる一方で、金利はローンの額に応じて異なります。

- 参照モデル
1つのモデルには、読取り専用の配列である参照モデルを複数含めることができます。ルールは、複数の参照モデルのセルを参照できます。ルールでセルを更新または挿入できるのは、メイン・モデルと呼ばれる1つの多次元配列のみです。参照モデルを使用すると、異なるディメンションにモデルを関係付けることができます。たとえば、ファクト表ft(country, year, sales)の他に、通貨の換算率の表cr(country, ratio)があるとします。この表には、ディメンション列としてcountry、メジャーとしてratioがあります。この表の各行は、当該国の通貨のUSドルへの換算率を示します。この2つの表は、次のルールで使用できます。

dollar_sales['Spain',2001] = sales['Spain',2000] * ratio['Spain']

- スケーラブルな計算
データをパーティション化して、他のパーティションとは関係なく各パーティション内のルールを評価できます。これにより、パーティションに基づいてモデル計算をパラレル化できます。たとえば、次のモデルがあるとします。

MODEL PARTITION BY (country) DIMENSION BY (year) MEASURES (sales)
(sales[year=2001] = AVG(sales)[year BETWEEN 1990 AND 2000]

データはcountryでパーティション化されています。各パーティション内では、2001年の売上高は1990年から2000年の平均売上高になるよう計算されます。パーティションはパラレルに処理できるため、スケーラブルなモデルの実行が実現します。

SQLモデリングの基本的なトピック

この項では、モデルの基本的な概念および使用方法について説明します。次の内容が含まれます。

- ベース・スキーマ

- MODEL句の構文

- SQLモデリングのキーワード

- セル参照

- ルール

- ルールの評価順序

- ルールのグローバルおよびローカル・キーワード

- UPDATE、UPSERTおよびUPSERT ALLの動作

- NULLおよび欠損セルの処理

- 参照モデル

ベース・スキーマ

この章の例は、shサンプル・スキーマから導出される次のビューsales_viewをベースにしています。

CREATE VIEW sales_view AS
SELECT country_name country, prod_name product, calendar_year year,
SUM(amount_sold) sales, COUNT(amount_sold) cnt,
MAX(calendar_year) KEEP (DENSE_RANK FIRST ORDER BY SUM(amount_sold) DESC)
OVER (PARTITION BY country_name, prod_name) best_year,
MAX(calendar_year) KEEP (DENSE_RANK LAST ORDER BY SUM(amount_sold) DESC)
OVER (PARTITION BY country_name, prod_name) worst_year
FROM sales, times, customers, countries, products
WHERE sales.time_id = times.time_id AND sales.prod_id = products.prod_id AND
sales.cust_id =customers.cust_id AND customers.country_id=countries.country_id
GROUP BY country_name, prod_name, calendar_year;

この問合せでは、country、productおよびyearでグルーピングされた売上データのSUMおよびCOUNT集計を計算します。また、対象国で各製品の売上高が最も高かった年をレポートします。これを製品のbest_yearと呼びます。また、worst_yearは、売上高が最も低かった年を示します。

MODEL句の構文

MODEL句を使用すると、SQL問合せブロックでデータの多次元計算を定義できます。多次元アプリケーションでは、ファクト表は、従属メジャーまたは属性として機能する列とともに各行を一意に識別する列から構成されます。MODEL句では、多次元配列を定義するPARTITION、DIMENSIONおよびMEASUREの各列、この多次元配列を処理するルール、および処理オプションを指定できます。

MODEL句は、パーティション内の配列計算を表す更新のリストを含み、SQL問合せブロックの一部となります。この句の構造を次に示します。

MODEL
[<global reference options>]
[<reference models>]
[MAIN <main-name>]
[PARTITION BY (<cols>)]
DIMENSION BY (<cols>)
MEASURES (<cols>)
[<reference options>]
[RULES]  <rule options>
(<rule>, <rule>,.., <rule>)
<global reference options> ::= <reference options> <ret-opt>
<ret-opt> ::= RETURN {ALL|UPDATED} ROWS
<reference options> ::=
[IGNORE NAV | [KEEP NAV]
[UNIQUE DIMENSION | UNIQUE SINGLE REFERENCE]
<rule options> ::=
[UPDATE | UPSERT | UPSERT ALL]
[AUTOMATIC ORDER | SEQUENTIAL ORDER]
[ITERATE (<number>)  [UNTIL <condition>]]
<reference models> ::= REFERENCE ON <ref-name> ON (<query>)
DIMENSION BY (<cols>) MEASURES (<cols>) <reference options>

各ルールは割当てを表します。ルールの左辺は、セルまたはセルの集合を参照します。ルールの右辺には、定数、ホスト変数、個々のセルまたはセルの範囲による集計を含む式を指定できます。次に、例23-1を考えてみます。

例23-1 MODEL句を使用した単純な問合せ

SELECT SUBSTR(country,1,20) country, SUBSTR(product,1,15) product, year, sales
FROM sales_view
WHERE country in ('Italy', 'Japan')
MODEL
RETURN UPDATED ROWS
MAIN simple_model
PARTITION BY (country)
DIMENSION BY (product, year)
MEASURES (sales)
RULES
(sales['Bounce', 2001] = 1000,
sales['Bounce', 2002] = sales['Bounce', 2001] + sales['Bounce', 2000],
sales['Y Box', 2002] = sales['Y Box', 2001])
ORDER BY country, product, year;

この問合せでは、国ItalyおよびJapanのsales_viewの行についてモデル計算を定義しています。このモデルには、simple_modelという名前が付いています。countryでデータをパーティション化し、各パーティション内にproductとyearで2次元配列を定義しています。この配列の各セルは、salesメジャーの値を保持します。このモデルの最初のルールでは、2001年のBounceの売上を1000に設定しています。残りの2つのルールでは、2002年のBounceの売上は2001年と2000年の売上の合計であり、2002年のY Boxの売上は前年の売上と同じであることを定義しています。

RETURN UPDATED ROWSを指定すると、前述の問合せは、モデル計算によって更新または挿入された行のみを戻します。デフォルトのRETURN ALL ROWSを使用した場合、MODEL句によって更新または挿入された行のみでなく、すべての行を取得します。問合せの出力は次のようになります。

COUNTRY              PRODUCT               YEAR      SALES
-------------------- --------------- ---------- ----------
Italy                Bounce                2001       1000
Italy                Bounce                2002    5333.69
Italy                Y Box                 2002   81207.55
Japan                Bounce                2001       1000
Japan                Bounce                2002    6133.53
Japan                Y Box                 2002   89634.83

MODEL句は、データベース表に対して行の更新または挿入を行っていないことに注目してください。次の問合せで、sales_viewが変更されていないことを示し、このことを確認します。

SELECT SUBSTR(country,1,20) country, SUBSTR(product,1,15) product, year, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan');

COUNTRY              PRODUCT               YEAR      SALES
-------------------- --------------- ---------- ----------
Italy                Bounce                1999    2474.78
Italy                Bounce                2000    4333.69
Italy                Bounce                2001     4846.3
...

MODEL句で行った2001年のBounceの売上値の更新は、データベースに反映されていません。データベース表の行を更新または挿入するには、INSERT、UPDATEまたはMERGE文を使用する必要があります。

前述の例では、PARTITION BY、DIMENSION BYおよびMEASURESリストで列を指定しています。パーティション・キー、ディメンション・キーおよびメジャーとして、定数、ホスト変数、単一行関数、集計関数、分析関数またはこれらを含む式を指定することもできます。ただし、これらはPARTITION BY、DIMENSION BYおよびMEASURESリスト内で別名化する必要があります。ルール、SELECTリストおよびORDER BY問合せ内の式を参照するには、別名を使用する必要があります。次の例では、式および別名の使用方法を示します。

SELECT country, p product, year, sales, profits
FROM sales_view
WHERE country IN ('Italy', 'Japan')
MODEL
RETURN UPDATED ROWS
PARTITION BY (SUBSTR(country,1,20) AS country)
DIMENSION BY (product AS p, year)
MEASURES (sales, 0 AS profits)
RULES
(profits['Bounce', 2001] = sales['Bounce', 2001] * 0.25,
sales['Bounce', 2002] = sales['Bounce', 2001] + sales['Bounce', 2000],
profits['Bounce', 2002] = sales['Bounce', 2002] * 0.35)
ORDER BY country, year;

COUNTRY   PRODUCT    YEAR   SALES         PROFITS
-------   ---------  ----   --------     --------
Italy     Bounce     2001     4846.3     1211.575
Italy     Bounce     2002    9179.99    3212.9965
Japan     Bounce     2001     6303.6       1575.9
Japan     Bounce     2002   11437.13    4002.9955

別名「0 AS profits」は、profitsメジャーのすべてのセルを0に初期化します。MODEL句の構文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

SQLモデリングのキーワード

この項では、SQLモデリングで使用されるキーワードの定義について説明します。

値の割当てとNULLの処理

- UPSERT
既存のセルのメジャー値を更新します。目的のセルが存在せず、なおかつルールが適切な表記法を使用している場合は、そのセルが挿入されます。セル参照にシンボリック参照が1つでも含まれる場合、セルの挿入は行われません。

- UPSERT ALL
UPSERTと似ていますが、より幅広いルール表記法で新しいセルを挿入できる点が異なります。

- UPDATE
既存のセルの値を更新します。セルの値が存在しない場合、更新は行われません。

- IGNORE NAV
数値セルの場合、使用できない値を0として処理します。つまり、問合せ結果セットによってMODEL句に指定されていないセルは、計算でゼロとして処理されます。これは、モデルのすべてのメジャーに対してグローバル・レベルで使用できます。

- KEEP NAV
使用できないセルの値を変更せずにそのまま維持します。これは、グローバル・レベルでIGNORE NAVを指定した場合に例外を設けるのに役立ちます。これはデフォルトなので、省略可能です。

計算定義

- MEASURES
モデルで変更または作成される値の集合です。

- RULES
値をメジャーに割り当てる式です。

- AUTOMATIC ORDER
すべてのルールが、論理的な依存関係に基づいた順序で評価されます。

- SEQUENTIAL ORDER
ルールが、記述された順序で評価されます。これがデフォルトです。

- UNIQUE DIMENSION
これがデフォルトです。MODEL句のPARTITION BYおよびDIMENSION BYの列の組合せが、モデル内のあらゆるセルを一意に識別する必要があることを意味します。この一意性は、必要に応じて、問合せの実行時に明示的に検証されます。その場合、処理速度低下の原因となることがあります。

- UNIQUE SINGLE REFERENCE
PARTITION BYおよびDIMENSION BY句は、ルールの右辺のシングル・ポイント参照を一意に識別します。これは、問合せの実行時に一意性の明示的なチェックを行わないため、処理時間を短縮できる場合があります。

- RETURN [ALL|UPDATED] ROWS
選択した行すべてを戻すか、ルールによって更新された行のみを戻すかを指定できます。デフォルト値は、ALLです。UPDATED ROWSも指定可能です。

セル参照

MODEL句では、リレーションはセルの多次元配列として扱われます。多次元配列のセルにはメジャー値が含まれ、このセルは、PARTITION BYキーによって定義された各パーティション内でDIMENSION BYキーを使用して索引付けされます。次の例を考えてみます。

SELECT country, product, year, sales, best_year, best_year
FROM sales_view
MODEL
PARTITION BY (country)
DIMENSION BY (product, year)
MEASURES (sales, best_year)
(<rules> ..)
ORDER BY country, product, year;

この例では、countryでデータをパーティション化し、各パーティション内にproductとyearで2次元配列を定義しています。この配列のセルには、salesおよびbest_yearという2つのメジャーが格納されます。

DIMENSION BYキーを指定してセルのメジャー値にアクセスすることを、セル参照といいます。セル参照の例を次に示します。

sales[product= 'Bounce', year=2000]

この例では、製品Bounceおよび年2000で参照されるセルのsales値にアクセスしています。セル参照では、DIMENSION BYキーを、前述のセル参照のようにシンボリックに指定するか、sales['Bounce', 2000]のように位置ベースで指定できます。

シンボリック・ディメンション参照

シンボリック・ディメンション参照(シンボリック参照)では、DIMENSION BYキーの値をブール式で指定します。たとえば、セル参照sales[year >= 2001]の場合、DIMENSION BYキーyearのシンボリック参照があり、yearの値が2001以上のセルをすべて指定します。また、sales[product = 'Bounce', year >= 2001]も、productおよびyearディメンションのシンボリック参照の一例です。

位置ベースのディメンション参照

位置ベースのディメンション参照(位置参照)は、定数、またはディメンションに対して指定された定数式です。たとえば、セル参照sales['Bounce']の場合、productディメンションの位置参照があり、製品Bounceのsales値にアクセスします。セル参照の定数(または定数式)は、DIMENSION BYキーに対して指定された列の順序と比較されます。次に、ディメンションの位置参照の使用例を示します。

sales['Bounce', 2001]

DIMENSION BYキーがproductとyearで両者の順序が同じ場合、Bounceと2001のsales値にアクセスします。

指定方法に応じて、セル参照はシングル・セル参照とマルチセル参照に分類されます。

ルール

モデル計算はルールで記述します。このルールに基づいて、PARTITION BY、DIMENSION BYおよびMEASURES句で定義された多次元配列のセルの操作が行われます。ルールは、左辺が1つのセルまたはセルの範囲を表し、右辺が定数、バインド変数、個々のセル、セルの範囲の集計関数を含む式になっている代入文です。ルールでは、表現力を最大限に高めるためにワイルド・カードおよびループ・コンストラクトを使用できます。次に、ルールの例を示します。

sales['Bounce', 2003] = 1.2 * sales['Bounce', 2002]

このルールは、製品Bounceの2003年の売上は2002年の売上より20%増加するということを表しています。

このルールは、左辺と右辺がともにシングル・セル参照であるため比較的単純です。マルチセル参照、集計、ネステッド・セル参照を使用した複雑なルールも記述できます。以降の項では、これらについて説明します。

シングル・セル参照

このタイプのルールには、左辺に定数によるシングル・セル参照があり、右辺にシングル・セル参照があるものが含まれます。次に、例をいくつか示します。

sales[product='Finding Fido', year=2003] = 100000
sales['Bounce', 2003] = 1.2 * sales['Bounce', 2002]
sales[product='Finding Fido', year=2004] = 0.8 * sales['Standard Mouse Pad',
year=2003] + sales['Finding Fido', 2003]

右辺のマルチセル参照

マルチセル参照は、ルールの右辺で使用できます。この場合、集計関数を適用して単一値に変換する必要があります。分析集計関数(逆パーセンタイル関数、仮説ランク関数、仮説分布関数など)や統計集計関数(CORRELATION関数、REGR_SLOPE関数など)など既存のすべての集計関数、およびユーザー定義の集計関数を使用できます。RANKやMOVING_AVGなどのウィンドウ関数も使用可能です。たとえば、次のルールでは、2003年のBounceの売上は、1998年から2002年の期間における最高の売上より100増加すると計算します。

sales['Bounce', 2003] = 100 + MAX(sales)['Bounce', year BETWEEN 1998 AND 2002]

次に、逆パーセンタイル関数PERCENTILE_DISCの使用例を示します。ここでは、2003年のFinding Fidoの売上高は、2003年より前のすべての年におけるFinding Fido、Standard Mouse PadおよびBoatの売上の中央値より30%増加すると想定しています。

sales[product='Finding Fido', year=2003] = 1.3 *
PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY sales) [product IN ('Finding
Fido','Standard Mouse Pad','Boat'), year < 2003]

集計関数は、ルールの右辺でのみ使用できます。集計関数の引数には、MODEL句のメジャー、バインド変数、定数、またはこれらを含む式を指定できます。たとえば、次のルールでは、2003年のBounceの売上は、1998年から2002年の売上の加重平均になると計算します。

sales['Bounce', 2003] =
AVG(sales * weight)['Bounce', year BETWEEN 1998 AND 2002]

左辺のマルチセル参照

ルールでは、次のように左辺にマルチセル参照を指定できます。

sales['Standard Mouse Pad', year > 2000] =
0.2 * sales['Finding Fido', year=2000]

このルールでは、左辺のセルの範囲(製品Standard Mouse Padのセルおよび2001年以降のyearのセル)にアクセスして、これらのセルのsalesメジャーを右辺の式で計算される値に割り当てます。前述のルールによる計算は、「2001年以降のStandard Mouse Padの売上は、2000年のFinding Fidoの売上の20%になる」ということを表しています。この計算は、右辺のセル参照(右辺の式)が左辺で参照されるすべてのセルと同じである点で単純です。

CV関数の使用

CV関数を使用すると相対索引付けが可能です。相対索引付けでは、左辺で参照されるセルのディメンション値が右辺のセル参照で使用されます。CV関数は、引数にディメンション・キーを取り、左辺で現在参照されているセルのDIMENSION BYキーの値を提供します。次の例を考えてみます。

sales[product='Standard Mouse Pad', year>2000] =
sales[CV(product), CV(year)] + 0.2 * sales['Finding Fido', 2000]

左辺がセルStandard Mouse Padおよび2001を参照する場合、右辺の式は次のようになります。

sales['Standard Mouse Pad', 2001] + 0.2 * sales['Finding Fido', 2000]

同様に、左辺がセルStandard Mouse Padおよび2002を参照する場合、右辺の式は次のようになります。

sales['Standard Mouse Pad', 2002] + 0.2 * sales['Finding Fido', 2000]

また、CV()のように引数を指定せずにCVを使用することもできます。この場合、位置参照となります。CV()は、セル参照の外部で使用することもできます。ただし、この場合は、引数に対象のディメンション名を指定する必要があります。前述のルールは、次のように記述することもできます。

sales[product='Standard Mouse Pad', year>2000] =
sales[CV(), CV()] + 0.2 * sales['Finding Fido', 2000]

最初のCV()参照はCV(product)に対応し、2番目の参照はCV(year)に対応します。CV関数は、右辺のセル参照でのみ使用できます。別のCV関数の使用例を次に示します。

sales[product IN ('Finding Fido','Standard Mouse Pad','Bounce'), year
BETWEEN 2002 AND 2004] = 2 * sales[CV(product), CV(year)-10]

このルールは、「Finding Fido、Standard Mouse Pad、Bounceの各製品について、2002年から2004年の売上は10年前の2倍になる」という意味です。

ANYワイルド・カードの使用

セル参照でワイルド・カードANYを使用すると、NULLを含むすべてのディメンション値に一致させることができます。ANYは、ルールの左辺と右辺の両方で使用できます。たとえば、「2003年の全製品の売上は、2002年の売上より10%増加する」という計算を行う場合、ルールは次のようになります。

sales[product IS ANY, 2003] = 1.1 * sales[CV(product), 2002]

位置参照を使用する場合は、次のようになります。

sales[ANY, 2003] = 1.1 * sales[CV(), 2002]

ANYは、位置ベースで指定した場合でも、シンボリック参照として扱われます。これは、ANYの実際の意味が(dimension IS NOT NULL OR dimension IS NULL)であるためです。

ネステッド・セル参照

セル参照はネストできます。つまり、ディメンション値を指定するセル参照をセル参照内で使用できます。たとえば、ネステッド・セル参照のメジャーがbest_yearの場合、次のようになります。

sales[product='Bounce', year = best_year['Bounce', 2003]]

ネステッド・セル参照best_year['Bounce', 2003]は、ディメンション・キーyearに値を提供し、yearのシンボリック参照で使用されます。メジャーbest_yearおよびworst_yearは、年(y)と製品(p)の組合せごとに、製品pの売上が最高または最低だった年を示します。次のルールでは、2003年のStandard Mouse Padの売上高が、Finding Fidoの売上が最高および最低だった年のStandard Mouse Padの平均売上高となるものとして計算します。

sales['Standard Mouse Pad', 2003] = (sales[CV(), best_year['Finding Fido',
CV(year)]] + sales[CV(), worst_year['Finding Fido', CV(year)]]) / 2

Oracle Databaseで許容されるネストのレベルは1つのみです。また、ネステッド・セル参照として使用できるのは、シングル・セル参照のみです。マルチセル参照の集計は、ネステッド・セル参照で使用できません。

ルールの評価順序

デフォルトでは、ルールはMODEL句内での出現順に評価されます。MODEL句でオプションのキーワードSEQUENTIAL ORDERを指定すると、評価順序を明示できます。ルールの評価順序が順次的になっているSQLモデルを、順次順序モデルと呼びます。たとえば、次のRULESの指定では、指定した順序でルールが評価されます。

RULES SEQUENTIAL ORDER
(sales['Bounce', 2001] =
sales['Bounce', 2000] + sales['Bounce', 1999],   --Rule R1
sales['Bounce', 2000] = 50000,                     --Rule R2
sales['Bounce', 1999] = 40000)                     --Rule R3

また、AUTOMATIC ORDERオプションを使用すると、Oracle Databaseによってルールの評価順序が自動的に決定されます。Oracleは、各ルール内のセル参照を調べ、ルール間の依存関係を確認します。ルールR1の左辺で参照されるセルが、別のルールR2の右辺で参照される場合、R2はR1に依存しているとみなされます。したがって、ルールR1は、ルールR2より先に評価される必要があります。前述の例にAUTOMATIC ORDERを指定すると、次のようになります。

RULES AUTOMATIC ORDER
(sales['Bounce', 2001] = sales['Bounce', 2000] + sales['Bounce', 1999],
sales['Bounce', 2000] = 50000,
sales['Bounce', 1999] = 40000)

ルール2とルール3は、ルール1より先に任意の順序で評価されます。これは、ルール1がルール2とルール3に依存しており、ルール2とルール3より後に評価される必要があるためです。ルール2とルール3は相互に依存していないため、評価順序は任意になります。相互に独立したルールの場合、任意の順序で評価できます。前述の例のように、評価順序が自動になっているSQLモデルを自動順序モデルと呼びます。

自動順序モデルの場合、同一セルへの複数の割当ては禁止されています。つまり、セルのメジャーを割り当てることができるのは1回のみです。結果が不確定的となる場合は、エラーが戻されます。たとえば、次のルール指定の場合、sales['Bounce', 2001]が複数回割り当てられているため、エラーが生成されます。

RULES AUTOMATIC ORDER
(sales['Bounce', 2001] = sales['Bounce', 2000] + sales['Bounce', 1999],
sales['Bounce', 2001] = 50000,
sales['Bounce', 2001] = 40000)

2001年の製品Bounceの売上を割り当てるルールは相互に依存していないため、ルール間で特定の評価順序が決まりません。この場合、sales['Bounce', 2001]は、40000、50000、1999年と2000年のBounceの合計売上のいずれにもなり得るため、評価順序が無原則となり、不確定的な結果になります。Oracle Databaseでは、これを防止するために、AUTOMATIC ORDERが指定された場合は複数の割当てを禁止しています。ただし、順次順序モデルでは、複数の割当ては可能です。前述の例でAUTOMATIC ORDERのかわりにSEQUENTIAL ORDERを指定した場合、sales['Bounce', 2001]の結果は40000になります。

ルールのグローバルおよびローカル・キーワード

UPDATE、UPSERT、UPSERT ALL、IGNORE NAVおよびKEEP NAVオプションは、RULES句の中でグローバル・レベルで指定できます。この場合、すべてのルールが指定のモードで処理されます。これらのオプションは、ルールごとにローカル・レベルで指定することもできます。この場合、グローバルな動作はオーバーライドされます。たとえば、次の指定を考えてみます。

RULES UPDATE
(UPDATE s['Bounce',2001] = sales['Bounce',2000] + sales['Bounce',1999],
UPSERT s['Y Box', 2001] = sales['Y Box', 2000] + sales['Y Box', 1999],
sales['Mouse Pad', 2001] = sales['Mouse Pad', 2000] +
sales['Mouse Pad',1999])

UPDATEオプションをグローバル・レベルで指定しているため、最初と3番目のルールは更新モードで処理されます。2番目のルールは、UPSERTキーワードを指定しているため、アップサート・モードで処理されます。3番目のルールは、オプションが未指定のため、グローバル・オプションの更新動作を継承します。

UPDATE、UPSERTおよびUPSERT ALLの動作

UPDATE、UPSERTまたはUPSERT ALLのいずれかを選択することで、ルールで指定されたセルの処理方法を指定できます。MODEL句のルールは、デフォルトでUPSERTとして処理されますが、アップサートであることを明確にするために、明示的にUPSERTキーワードを指定してもかまいません。

次の各項で、これら3つのオプションの動作について説明します。

- UPDATEの動作

- UPSERTの動作

- UPSERT ALLの動作

UPDATEの動作

UPDATEオプションを指定すると、完全な更新モードが強制されます。このモードでは、左辺の参照先のセルが存在しない場合、ルールは無視されます。ルールの左辺で参照されるセルが存在する場合、そのメジャーが右辺の式の値で更新されます。それ以外の場合でセル参照が位置ベースの場合は、右辺の式の値に等しいメジャー値で新しいセルが作成され、多次元配列に挿入されます。セル参照が位置ベースでない場合、セルは挿入されません。セルの指定にシンボリック参照が1つでも含まれる場合は、アップサートのルールでも挿入はできないことに注意してください。たとえば、次のルールがあるとします。

sales['Bounce', 2003] = sales['Bounce', 2001] + sales ['Bounce', 2002]

製品Bounceと2003年のセルが存在する場合、2001年と2002年のBounceの合計売上でセルが更新され、存在しない場合はセルが作成されます。次のように、シンボリック参照を使用して同じルールを作成した場合、更新は行われません。

sales[prod= 'Bounce', year= 2003] = sales['Bounce', 2001] + sales ['Bounce', 2002]

UPSERTの動作

UPSERTを使用すると、セルが存在せず、セル参照に含まれているのが定数で修飾された位置参照のみである場合に、ルールの左辺で参照されているセルに対応する新しいセルが作成されます。FORループで作成されるセル参照(「SQLモデリングの高度なトピック」を参照)は、位置参照として扱われることに注意してください。このため、FORループで作成される値は、新しいセルの挿入に使用されます。たとえば、2004年以降の年のセルがないとします。この場合、次のルールを考えてみます。

UPSERT sales['Bounce', year = 2004] = 1.1 * sales['Bounce', 2002]

この場合、シンボリック参照year = 2004があるため、新しいセルは作成されません。さらに、次の例を考えてみます。

UPSERT sales['Bounce', 2004] = 1.1 * sales['Bounce', 2002]

この場合、2004年の製品Bounceのセルが新規作成されます。ただし、いずれかの参照がANYの場合、新しいセルは作成されません。これは、ANYが、NULLを含むすべてのディメンション値を許可する述語であるためです。たとえば、ディメンションdに対する参照ANYは、(d IS NOT NULL OR d IS NULL)という述語と同じ意味になります。

UPSERTルールの左辺のセル参照でFORループが使用されている場合、アップサート対象のセルのリストは、各ディメンションのすべての個別値のクロス積を計算することで生成されます。FORループを含むUPSERTを使用してディメンションの稠密化(「レポート用のデータの稠密化」を参照)を行うことは可能ですが、稠密化にはパーティション外部結合操作を使用するのが一般的です。

UPSERT ALLの動作

UPSERT ALLを使用すると、存在述語(比較、IN、ANYなど)を左辺に含むモデル・ルールにUPSERTの動作を適用できます。たとえば次のルールでは、ANYを使用して、San Francisco、San JoseおよびOaklandの組合せであるBay Areaを作成しています。

SELECT product, time, city, s sales
FROM cube_subquery
MODEL PARTITION BY (product)
DIMENSION BY (time, city) MEASURES(sales s
RULES UPSERT ALL
(s[ANY, 'Bay Area'] =
s[CV(), 'San Francisco'] + s[CV(), 'San Jose'] + s[CV(), 'Oakland']
s['2004', ANY] = s['2002', CV()] + s['2003', CV()]);

この例の最初のルールでは、単純に、個別の各time値用のBay Areaセルを挿入しています。2番目のルールでは、Bay Areaを含む個別の各city値用の2004セルを挿入しています。この例は、左辺で使用されている存在述語がANY述語であるという比較的単純なものですが、UPSERT ALLはより複雑な計算で使用することも可能です。

UPSERT ALLを使用する場合(特に、複数のシンボリック・ディメンション参照がある場合)は、その動作を正確に理解しておく必要があります。その動作は、FORループを使用するUPSERTルールとは異なります。Oracle Databaseでは、UPSERT ALLルールの評価の際、次の手順が実行され、アップサート対象となるセル参照のリストが作成されます。

手順1   セルの検索

セル参照のすべてのシンボリック述語を満たす既存のセルを検索します。

手順2   個別ディメンション値の検索

シンボリック参照されているディメンションのみを対象に、これらのセルの重複しないディメンション値の組合せを検索します。

手順3   クロス積の実行

これらの値の組合せと、位置参照で指定されたディメンション値とのクロス積を実行します。

手順4   新しいセルのアップサート

手順3の結果を使用して、新しいセルを配列にアップサートします。

これらの4つの手順を説明するために、3つのディメンションを持つ抽象的なデータおよびモデルを使用した簡単な例を示します。このモデルは、(product, time, city)でディメンション化され、salesというメジャーを持つものとします。この例では、都市zに新しい売上値をアップサートしますが、この売上値は都市yからコピーしてきます。

UPSERT ALL sales[ANY, ANY, 'z']= sales[CV(product),CV(time),'y']

ソースのデータセットには、次の4つの行があります。

PROD    TIME   CITY   SALES
1    2002      x      10
1    2003      x      15
2    2002      y      21
2    2003      y      24

次に、前述の4つの手順を、このデータに当てはめて詳しく説明します。

1. ルールのシンボリック述語はANYなので、前述の行すべてが対象になります。

2. 条件に合致する、シンボリック述語で参照されたセルの重複しないディメンションの組合せは、(1, 2002)、(1, 2003)、(2, 2002)および(2, 2003)です。

3. これらのディメンションの組合せと、位置参照で指定されたセルのクロス積を求めます。この例では、単純に値zとのクロス積となり、結果のセル参照は(1, 2002, z)、(1, 2003, z)、(2, 2002, z)および(2, 2003, z)となります。

4. 手順3で求めたセルが、都市yに基づいて計算された売上でアップサートされます。都市yの製品1には値がないので、製品1用に作成されたセルの売上値はNULLになります。当然のことながら、ルールが異なる場合には、新しいすべてのセルに非NULLの結果が生成されることもあります。結果セットには、元の4つの行に加えて、次のように新しい4つの行が追加されます。

PROD    TIME   CITY    SALES
1    2002      x       10
1    2003      x       15
2    2002      y       21
2    2003      y       24
1    2002      z     NULL
1    2003      z     NULL
2    2002      z       21
2    2003      z       24

この結果は、すべてのディメンションの値をすべて使用したクロス積ではないことに注意してください。すべてのディメンションの値をすべて使用した場合は、(1,2002, y)、(2,2003, x)などのセルも生成されます。これは、あくまでも既存の行で見つかったディメンションの組合せを使用して生成された結果です。

NULLおよび欠損セルの処理

モデルを使用するアプリケーションでは、NULLによるセル・メジャーの不確定値のみでなく、欠損セルによる不確定性にも対応する必要があります。シングル・セル参照で参照されるセルがデータ内で欠損している場合、このようなセルを欠損セルと呼びます。MODEL句では、NULLのセルおよび欠損セルについてANSI SQL規格に準拠したデフォルトの処理方法があり、ビジネス・ロジックに応じて適切な方法でこうしたセルを処理するオプションも用意されています。たとえば、NULLを算術演算でゼロとして処理するオプションなどがあります。

デフォルトでは、NULLのセル・メジャー値は、SQLの他の箇所と同じ方法で処理されます。たとえば、次のルールがあるとします。

sales['Bounce', 2001] = sales['Bounce', 1999] + sales['Bounce', 2000]

1999年と2000年のBounceの売上値のいずれかがNULLの場合、右辺の式はNULLと評価されます。同様に、ルール内の集計関数でも、通常の動作と同じ方法でNULL値を処理します。つまり、NULL値は集計処理時に無視されます。

欠損セルは、NULLメジャー値を含むセルとして処理されます。たとえば、前述のルールでは、Bounceと2000のセルがない場合、NULL値として処理され、右辺の式はNULLと評価されます。

欠損セルとNULLの区別

PRESENTV関数およびPRESENTNNV関数を使用すると、欠損セルの識別およびNULL値との区別が可能です。これらの関数の引数には、PRESENTV(cell, expr1, expr2)のようにシングル・セル参照および2つの式を指定します。PRESENTV関数は、セルcellがMODEL句に入力されたデータに存在する場合、最初の式expr1を戻します。それ以外の場合は、2番目の式expr2を戻します。次の例を考えてみます。

PRESENTV(sales['Bounce', 2000], 1.1*sales['Bounce', 2000], 100)

製品Bounceおよび2000年のセルが存在する場合、対応する売上値に1.1を掛けて戻します。これに該当しない場合は100を戻します。製品Bounceおよび2000年のセルがNULLの場合、前述の指定ではNULLが戻されます。

PRESENTNNV関数は、セルの有無のチェックに加えて、セルがNULLかどうかのチェックも行います。セルが存在してNULLでない場合は最初の式expr1を、それ以外の場合は2番目の式expr2を戻します。次の例を考えてみます。

PRESENTNNV(sales['Bounce', 2000], 1.1*sales['Bounce', 2000], 100)

この例では、sales['Bounce', 2000]が存在し、NULLでない場合、1.1*sales['Bounce', 2000]を戻します。それ以外の場合は100を戻します。

アプリケーションでは、モデルにIS PRESENT述語を使用して、セルの有無を明示的にチェックできます。この述語は、セルが存在する場合はTRUEを、存在しない場合はFALSEを戻します。PRESENTNNVを使用した前述の例を、IS PRESENTを使用して記述すると次のようになります。

CASE WHEN sales['Bounce', 2000] IS PRESENT AND sales['Bounce', 2000] IS NOT NULL
THEN 1.1 * sales['Bounce', 2000]
ELSE 100
END

PRESENTVおよびPRESENTNNV関数と同様に、IS PRESENT述語は、入力データ(MODEL句の実行前に存在していたデータ)にセルが存在するかどうかをチェックします。これを使用すると、UPSERTルールで新規に挿入されたセルの複数のメジャーを初期化できます。たとえば、製品Bounceと2003年のセルがデータに存在しない場合、セルのsales値とprofit値をそれぞれ1000と500に初期化するには、次のように記述します。

RULES
(UPSERT sales['Bounce', 2003] =
PRESENTV(sales['Bounce', 2003], sales['Bounce', 2003], 1000),
UPSERT profit['Bounce', 2003] =
PRESENTV(profit['Bounce', 2003], profit['Bounce', 2003], 500))

この例で使用されているPRESENTV関数は、入力データ内のセルの有無に応じて、TRUEまたはFALSEを戻します。評価順序に基づいて一方のルールでBounceと2003のセルが挿入される場合でも、他方のルールのPRESENTV関数は、引き続きFALSEと評価します。この動作は、ルール評価の事前処理手順とみなすことができます。この手順では、PRESENTV関数、PRESENTNNV関数およびIS PRESENT述語のすべてを評価し、対応する値ですべて置き換えます。

欠損セルおよびNULLのデフォルト値の使用

MODEL句では、デフォルトで欠損セルを、NULLメジャー値を含むセルとして処理します。この動作にするには、オプションのKEEP NAVキーワードをMODEL句で指定します。アプリケーションのデフォルトとして、欠損セルおよびNULLをなんらかの値にするには、IS PRESENT述語、IS NULL述語、PRESENTV関数およびPRESENTNNV関数を使用します。ただし、シングル・セル参照およびルールが多い場合は、処理が複雑になる場合があります。デフォルトのKEEP NAVオプションのかわりに、IGNORE NAVオプションを使用すると、NULLおよび欠損セルを次のデフォルト値に設定できます。

- 数値データの場合は0

- 文字/文字列データの場合は空の文字列

- 日付型のデータの場合は01-JAN-2001

- 前述以外のデータ型の場合はNULL

次の問合せを考えてみます。

SELECT product, year, sales
FROM sales_view
WHERE country = 'Poland'
MODEL
DIMENSION BY (product, year) MEASURES (sales sales) IGNORE NAV
RULES UPSERT
(sales['Bounce', 2003] = sales['Bounce', 2002] + sales['Bounce', 2001]);

この例では、MODEL句の入力データに、製品Bounceおよび2002年のセルがありません。IGNORE NAVオプションが指定されていることにより、sales['Bounce', 2002]のデフォルト値はNULLではなく、0になります(salesが数値型のため)。したがって、sales['Bounce', 2003]の値は、sales['Bounce', 2001]の値と同じになります。

セル参照でのNULLの使用

セル参照でNULL値を使用するには、次のいずれかの方法を使用します。

- ワイルド・カードANYを使用した位置参照(たとえば、sales[ANY])

- IS ANY述語を使用したシンボリック参照(たとえば、sales[product IS ANY])

- NULLの位置参照(たとえば、sales[NULL])

- IS NULL述語を使用したシンボリック参照(たとえば、sales[product IS NULL])

シンボリック参照sales[product = NULL]では、productディメンションのNULLのチェックは行われません。この場合の動作は、SQLによるNULLの標準の取扱いに準拠します。

参照モデル

ルールが処理される多次元配列の他に、1つ以上の読取り専用の多次元配列があります。前者は、メイン・モデル、後者は参照モデルと呼ばれます。参照モデルは、MODEL句で作成および参照でき、メイン・モデルの参照表のように機能します。参照モデルは、メイン・モデルと同様に問合せブロックで定義されます。また、参照モデルには、ディメンションとメジャーを示すDIMENSION BY句およびMEASURES句があります。参照モデルは、次の副次句で作成されます。

REFERENCE model_name ON (query) DIMENSION BY (cols) MEASURES (cols)
[reference options]

メイン・モデルと同様、参照モデルの多次元配列は、ルールの評価前に作成されます。ただし、メイン・モデルとは異なり、参照モデルは読取り専用のため、作成した後にセルの更新や新しいセルの挿入を行うことはできません。したがって、メイン・モデルのルールは、参照モデルに対して、セルへのアクセスは可能ですが、セルの更新や新しいセルの挿入はできません。次に、参照モデルとして通貨の換算表を使用した例を示します。

CREATE TABLE dollar_conv_tbl(country VARCHAR2(30), exchange_rate NUMBER);
INSERT INTO dollar_conv_tbl VALUES('Poland', 0.25);
INSERT INTO dollar_conv_tbl VALUES('France', 0.14);
...

2003年のPolandとFranceの算出売上をUSドルに換算するには、次のようにドル換算表を参照モデルとして使用できます。

SELECT country, year, sales, dollar_sales
FROM sales_view
GROUP BY country, year
MODEL
REFERENCE conv_ref ON (SELECT country, exchange_rate FROM dollar_conv_tbl)
DIMENSION BY (country) MEASURES (exchange_rate) IGNORE NAV
MAIN conversion
DIMENSION BY (country, year)
MEASURES (SUM(sales) sales, SUM(sales) dollar_sales) IGNORE NAV
RULES
(dollar_sales['France', 2003] = sales[CV(country), 2002] * 1.02 *
conv_ref.exchange_rate['France'],
dollar_sales['Poland', 2003] =
sales['Poland', 2002] * 1.05 * exchange_rate['Poland']);

この例の要点を次に示します。

- 1つのディメンション参照モデルconv_refが表dollar_conv_tblの行に作成され、そのメジャーexchange_rateは、メイン・モデルのルールで参照されています。

- メイン・モデルconversionには、countryとyearの2つのディメンションがあり、参照モデルconv_refには、ディメンションcountryが1つあります。

- 参照モデルのメジャーexchange_rateへのアクセス方法に違いがあります。Franceの場合、model_name.measure_nameの表記法に準拠したconv_ref.exchange_rateを使用しているため明示的です。Polandの場合、単純なmeasure_nameの表記法に準拠した参照exchange_rateを使用しています。メイン・モデルと参照モデル間での列名の曖昧性を解決するには、前者の表記法を使用する必要があります。

次の例では、増加率がルールでハードコード化されています。Franceの増加率は2%、Polandは5%です。ただし、これらは別個の表に存在するため、各表の上位に参照モデルを定義できます。表growth_rate(country, year, rate)を次のように定義するとします。

CREATE TABLE growth_rate_tbl(country VARCHAR2(30),
year NUMBER, growth_rate NUMBER);
INSERT INTO growth_rate_tbl VALUES('Poland', 2002, 2.5);
INSERT INTO growth_rate_tbl VALUES('Poland', 2003, 5);
...
INSERT INTO growth_rate_tbl VALUES('France', 2002, 3);
INSERT INTO growth_rate_tbl VALUES('France', 2003, 2.5);

次の問合せでは、すべての国を対象とした2003年のドルでの算出売上高を計算します。

SELECT country, year, sales, dollar_sales
FROM sales_view
GROUP BY country, year
MODEL
REFERENCE conv_ref ON
(SELECT country, exchange_rate FROM dollar_conv_tbl)
DIMENSION BY (country c) MEASURES (exchange_rate) IGNORE NAV
REFERENCE growth_ref ON
(SELECT country, year, growth_rate FROM growth_rate_tbl)
DIMENSION BY (country c, year y) MEASURES (growth_rate) IGNORE NAV
MAIN projection
DIMENSION BY (country, year) MEASURES (SUM(sales) sales, 0 dollar_sales)
IGNORE NAV
RULES
(dollar_sales[ANY, 2003] = sales[CV(country), 2002] *
growth_rate[CV(country), CV(year)] *
exchange_rate[CV(country)]);

この問合せでは、異なるディメンションのオブジェクトを処理および関連付ける、MODEL句の機能を示しています。参照モデルconv_refにはディメンションが1つあり、参照モデルgrowth_refとメイン・モデルにはディメンションが2つあります。参照モデルでのシングル・セル参照のディメンションは、CV関数を使用して指定されているので、メイン・モデルのセルと参照モデルが関連付けられます。この指定により、メイン・モデルと参照モデル間でリレーショナル結合が実行されます。

参照モデルは、キーを順序番号に変換し、順序番号を使用した計算(たとえば、減算に前期を使用する場合など)を実行してから、順序番号をキーに再変換する場合にも有効です。たとえば、順序番号を年に割り当てる次のビューがあるとします。

CREATE or REPLACE VIEW year_2_seq (i, year) AS
SELECT ROW_NUMBER() OVER (ORDER BY calendar_year), calendar_year
FROM (SELECT DISTINCT calendar_year FROM TIMES);

このビューで、参照表を2つ定義できます。1つは、整数から年に変換するi2yです。これは、順序番号を整数にマップします。もう1つは、年から整数に変換するy2iで、逆のマッピングを行います。参照y2i.i[year]およびy2i.i[year] - 1は、それぞれ今年と昨年の順序番号を戻します。参照i2y.y[y2i.i[year]-1]は、昨年のyearキーの値を戻します。次の問合せでは、参照モデルのこのような使用方法を示します。

SELECT country, product, year, sales, prior_period
FROM sales_view
MODEL
REFERENCE y2i ON (SELECT year, i FROM year_2_seq) DIMENSION BY (year y)
MEASURES (i)
REFERENCE i2y ON (SELECT year, i FROM year_2_seq) DIMENSION BY (i)
MEASURES (year y)
MAIN projection2 PARTITION BY (country)
DIMENSION BY (product, year)
MEASURES (sales, CAST(NULL AS NUMBER) prior_period)
(prior_period[ANY, ANY] = sales[CV(product), i2y.y[y2i.i[CV(year)]-1]])
ORDER BY country, product, year;

前述の例では、参照モデルのセル参照がネストされていることがわかります。参照モデルy2iのセル参照は、i2yのセル参照内でネストされ、これがさらにメインSQLモデルのセル参照内でネストされています。参照モデルのセル参照は、無制限のレベルでネストできます。ただし、メインSQLモデルのセル参照の場合は、2つのレベルまでしかネストできません。

最後に、参照モデルの指定および使用方法に関する制限を次に示します。

- 参照モデルでは、PARTITION BY句は指定できません。

- 参照モデルが定義された問合せブロックは、他の問合せと相関させることができません。

- 参照モデルには、一意の名前を付ける必要があります。

- 参照モデルのセルへの参照は、すべてシングル・セル参照である必要があります。

SQLモデリングの高度なトピック

この項では、SQLモデリングのより高度なトピックについて説明します。

- FORループ

- 反復モデル

- AUTOMATIC ORDERモデルでのルールの依存関係

- 順序付きルール

- 分析関数

- UNIQUE DIMENSIONとUNIQUE SINGLE REFERENCE

FORループ

MODEL句では、ルール内でFORコンストラクトを使用して、計算をよりコンパクトに表現できます。これは、ルールの左辺と右辺の両方で使用できます。FORループは、ルールの左辺に指定された場合は位置参照として扱われます。たとえば、次の計算を考えてみます。この計算では、2004年の製品の売上高は、2003年の売上高より10%増加すると見積ります。

RULES UPSERT
(sales['Bounce', 2004] = 1.1 * sales['Bounce', 2003],
sales['Standard Mouse Pad', 2004] = 1.1 * sales['Standard Mouse Pad', 2003],
...
sales['Y Box', 2004] = 1.1 * sales['Y Box', 2003])

この計算では、UPSERTオプションが使用されているため、これらの製品および2004年のセルが多次元配列にない場合、これらのセルが挿入されます。この場合、製品の数と同じ数のルールを指定する必要があるため、処理が困難になります。次のように、FORコンストラクトを使用すると、この計算をコンパクトかつ完全に同じセマンティックで表現できます。

RULES UPSERT
(sales[FOR product IN ('Bounce', 'Standard Mouse Pad', ..., 'Y Box'), 2004] =
1.1 * sales[CV(product), 2003])

この例と同様の指定をFORキーワードを使用せずに記述すると、次のようになります。

RULES UPSERT
(sales[product IN ('Bounce', 'Standard Mouse Pad', ..., 'Y Box'), 2004] =
1.1 * sales[CV(product), 2003])

この場合、UPSERTを指定していても、UPDATEの処理方法が適用されます。つまり、既存のセルは更新されますが、この指定では新しいセルは作成されません。その理由は、productに対するマルチセル参照がシンボリック参照であり、シンボリック参照では新しいセルを挿入できないためです。FORコンストラクトは、1つのルールから位置参照で複数のルールを生成するマクロとみなすことができます。これによりUPSERTの処理方法が保持されます。概念上、次のルールがあるとします。

sales[FOR product IN ('Bounce', 'Standard Mouse Pad', ..., 'Y Box'),
FOR year IN (2004, 2005)] = 1.1 * sales[CV(product), CV(year)-1]

これは、次のルールの順序付きコレクションとして扱うことができます。

sales['Bounce', 2004] = 1.1 * sales[CV(product), CV(year)-1],
sales['Bounce', 2005] = 1.1 * sales[CV(product), CV(year)-1],
sales['Standard Mouse Pad', 2004] = 1.1 *
sales[CV(product), CV(year)-1],
sales['Standard Mouse Pad', 2005] = 1.1 * sales[CV(product),
CV(year)-1],
...
sales['Y Box', 2004] = 1.1 * sales[CV(product), CV(year)-1],
sales['Y Box', 2005] = 1.1 * sales[CV(product), CV(year)-1]

前述の例の場合、FORコンストラクトの形式は、FOR ディメンション IN (値のリスト)となっています。リスト内の値は、定数の式やシングル・セル参照など、単一値の式でなければなりません。前述の例では、FORコンストラクトは、productとyearに別々にあります。単一のFORコンストラクトですべてのディメンションを指定し、複数列のINリストを使用して値を指定することも可能です。たとえば、2004年のBounceの売上高、2005年のStandard Mouse Padの売上高、2004年と2005年のY Boxの売上高のみを見積る場合、次のように記述できます。これは、次のように記述できます。

sales[FOR (product, year) IN (('Bounce', 2004), ('Standard Mouse Pad', 2005),
('Y Box', 2004), ('Y Box', 2005))] =
1.1 * sales[CV(product), CV(year)-1]

n個のディメンションd1, ..., dnがあり、リスト内にm個の値がある場合、このFORコンストラクトの形式は、FOR (d1, ..., dn) IN ((d1_val1, ..., dn_val1), ..., (d1_valm, ..., dn_valm)]とする必要があります。

FOR内のディメンションの値のリストは、表または副問合せから取得することもできます。このようなケースに対応するためにOracle Databaseでは、FOR ディメンション IN (副問合せ)などの形式のFORコンストラクトが用意されています。たとえば、対象の製品が表interesting_productsに格納されるとします。この場合、次のルールでは、2004年と2005年の製品の売上高を見積ります。

sales[FOR product IN (SELECT product_name FROM interesting_products)
FOR year IN (2004, 2005)] = 1.1 * sales[CV(product), CV(year)-1]

別の例として、new_countryという新しいcountryを導入し、その売上高を、Polandにおいて売上高が存在するすべての製品および年から流用するシナリオを考えてみます。これを実現するために、次の文を発行します。

SELECT country, product, year, s
FROM sales_view
MODEL
DIMENSION BY (country, product, year)
MEASURES (sales s) IGNORE NAV
RULES UPSERT
(s[FOR (country, product, year) IN
(SELECT DISTINCT 'new_country', product, year
FROM sales_view
WHERE country = 'Poland')] = s['Poland',CV(),CV()])
ORDER BY country, year, product;

この指定に含まれている、副問合せを評価することで生成される複数列のINリストに注目してください。INリストの取得に使用される副問合せは、外部の問合せブロックと相関させることはできません。

このルールで作成されるアップサートのリストは、各ディメンションの個別値のクロス積です。たとえば、countryの値が10個、yearの値が5個、productの値が3個ある場合は、150個のセルを含むアップサートのリストが生成されます。

対象の値が不連続な領域の値であるとわかっている場合は、FORコンストラクトのFOR dimension FROM value1 TO value2 [INCREMENT | DECREMENT] value3を使用できます。これを指定すると、value1からvalue2の範囲内にあり、value1から始まり、value3ずつ増加(または減少)する値を取得できます。value1、value2およびvalue3の各値には、単一値の式を指定する必要があります。たとえば、次のルールがあるとします。

sales['Bounce', FOR year FROM 2001 TO 2005 INCREMENT 1] =
sales['Bounce', year=CV(year)-1] * 1.2

これは、意味的および順序的に次のルールと同等です。

sales['Bounce', 2001] = sales['Bounce', 2000] * 1.2,
sales['Bounce', 2002] = sales['Bounce', 2001] * 1.2,
...
sales['Bounce', 2005] = sales['Bounce', 2004] * 1.2

このタイプのFORコンストラクトは、数値、日付および日時データ型のディメンションに使用できます。増加/減少式のvalue3の型には、数値型のディメンションの場合は数値を、日付または日時型のディメンションの場合は数値またはインターバルを指定する必要があります。また、value3は、正の値である必要があります。FOR year FROM 2005 TO 2001 INCREMENT -1を使用した場合、エラーが戻されます。この場合、FOR year FROM 2005 TO 2001 DECREMENT 1またはFOR year FROM 2001 TO 2005 INCREMENT 1を使用する必要があります。

文字列値を生成する場合は、FORコンストラクトのFOR dimension LIKE string FROM value1 TO value2 [INCREMENT | DECREMENT] value3を使用できます。文字列stringには、%文字を1つ含める必要があります。これを指定すると、value1からvalue2の範囲内にありvalue3の値ごとに増減する値で%を置き換えた文字列を取得できます。たとえば、次のルールがあるとします。

sales[FOR product LIKE 'product-%' FROM 1 TO 3 INCREMENT 1, 2003] =
sales[CV(product), 2002] * 1.2

これは、次のルールと同等です。

sales['product-1', 2003] = sales['product-1', 2002] * 1.2,
sales['product-2', 2003] = sales['product-2', 2002] * 1.2,
sales['product-3', 2003] = sales['product-3', 2002] * 1.2

SEQUENTIAL ORDERモデルの場合、FORコンストラクトで表されるルールは、生成された順に評価されます。一方、AUTOMATIC ORDERを指定した場合、ルールの評価順序は依存関係に基づいて決定されます。たとえば、次のようにルールで複数のルールが表される場合の評価順序を考えてみます。

sales['Bounce', FOR year FROM 2004 TO 2001 DECREMENT 1] =
1.1 * sales['Bounce', CV(year)-1]

SEQUENTIAL ORDERモデルの場合、ルールは次の順序で生成されます。

sales['Bounce', 2004] = 1.1 * sales['Bounce', 2003],
sales['Bounce', 2003] = 1.1 * sales['Bounce', 2002],
sales['Bounce', 2002] = 1.1 * sales['Bounce', 2001],
sales['Bounce', 2001] = 1.1 * sales['Bounce', 2000]

一方、AUTOMATIC ORDERモデルの場合、次と同等の順序になります。

sales['Bounce', 2001] = 1.1 * sales['Bounce', 2000],
sales['Bounce', 2002] = 1.1 * sales['Bounce', 2001],
sales['Bounce', 2003] = 1.1 * sales['Bounce', 2002],
sales['Bounce', 2004] = 1.1 * sales['Bounce', 2003]

FORループを含む式の評価

FORループ・コンストラクトは、1つのディメンション、またはすべてのディメンション(INリストで複数列を指定する場合)の単一値参照を生成するための反復の仕組みを提供します。左辺にFORループを含む式を評価するプロセスでは、基本的に、これらのFORループによって生成される各単一値参照について式の右辺を評価し、この単一値参照で指定されたセルにその結果を割り当てるという処理が行われます。これらの単一参照値の生成は、「FORループの展開」と呼ばれます。これら展開されたセルは、展開プロセス時の生成順で評価されます。

展開がどのように行われるかは、ルールに指定されたUPSERT、UPDATEまたはUPDATE ALLの動作と、そのルール固有の特性によって異なります。これを理解するためには、問合せの処理に伴う、問合せ計画の作成と問合せの実行という2つのステップを理解する必要があります。問合せ計画の作成では、効率的な問合せ実行計画を作成するために、特定のルール参照が解決されます。問合せの実行では、残りの未解決の参照がすべて解決されます。FORループの展開は、問合せ計画の作成時に行われる場合もあれば、問合せの実行時に行われる場合もあります。以降の各項で、展開方法がどのように決定されるかを詳しく説明します。

UPDATEルールおよびUPSERTルールでの展開

UPDATEルールまたはUPSERTルールでは、ルールの左辺の展開でシングル・セル参照が生成されることが保証される場合、展開は問合せの実行時に行われます。展開プロセスでシングル・セル参照を生成できない場合、展開は問合せ計画の作成時に行われ、展開プロセスで生成される参照ごとに同じ式のコピーが作成されます。たとえば、次の式では、展開によってシングル・セル参照が生成されるので、展開は問合せの実行時に行われます。

sales[FOR product IN ('prod1', 'prod2'), 2003] = sales[CV(product), 2002] * 1.2

一方、次の式では、別のディメンションに対する述語が使用されているため、参照値の展開で単一値参照は生成されません。

sales[FOR product in ('prod1', 'prod2'), year >= 2003]
= sales[CV(product), 2002] * 1.2

この式には、yearディメンションに対する単一値参照がありません。そのため、たとえFORループがproductディメンションで展開されるとしても、式の左辺に単一値参照は存在しないことになります。これは、展開が問合せ計画の作成時に行われ、元の式は物理的に次の式に置き換えられることを意味します。

sales['prod1', year >= 2003]  = sales[CV(product), 2002] * 1.2,
sales['prod2', year >= 2003]  = sales[CV(product), 2002] * 1.2

MODEL句内で実行される分析と最適化は、問合せ計画の作成時における展開の後(展開がこのタイミングで行われる場合)に行われます。したがって、これ以降のすべての処理は、あたかも複数のルールが明示的にMODEL句に指定されているかのように行われます。これらのケースでは、問合せ計画の作成時に展開を行うことによって、より正確な分析と、より効果的な式の評価の最適化が可能になります。ただし、この場合は式の数が増加する可能性があり、それによって式の合計数が上限を超えるとエラーが発生するので注意が必要です。

UPSERT ALLルールでの展開

UPSERT ALLを使用するルールでは、FORループは様々な方法で展開されます。UPSERT ALLルールのFORループは、使用されている述語に関係なく、常に問合せの実行時に展開されます。この動作によって、次の項で説明するFORループのいくつかの制限が回避されます。ただし、制限が少なくなることと、問合せ計画がより効果的に最適化されるようになることは、トレードオフの関係にあります。UPSERT ALLルールは、一般的に、同じような内容のUPSERTルールまたはUPDATEルールよりも処理が低速です。モデルを設計する際は、この点を考慮する必要があります。

式の左辺でFORループ式を使用する場合の制限事項

FORループ・コンストラクトの使用に関する制限事項は、展開処理が問合せ計画の作成時に行われるか、問合せの実行時に行われるかによって異なります。FORループを左辺に含む式が(前項で説明した理由によって)問合せ計画の作成時に展開される場合、展開するうえで評価する必要のある式は、問合せ計画の作成時に使用可能な値を持つ定数の式でなければなりません。たとえば、次の文を考えてみます。

sales[For product like 'prod%' from ITERATION_NUMBER
to ITERATION_NUMBER+1, year >= 2003] =  sales[CV(product), 2002]*1.2

UPSERT ALLが指定されていない場合、このルールは問合せ計画の作成時に展開されます。開始式と終了式を評価するために必要なITERATION_NUMBERの値は、問合せ計画の作成時には不明であるため、このルールが問合せの実行時に展開されない場合には、エラーが発生します。しかし、次のルールであれば、問合せ計画の作成時にエラーなしで展開できます。ITERATION_NUMBERがFORループ内の式として指定されていますが、このルールの場合、その値は展開するために必要ではありません。

sales[For product in ('prod'||ITERATION_NUMBER, 'prod'||(ITERATION_NUMBER+1)),
year >= 2003] =  sales[CV(product), 2002]*1.2

次のいずれかを含む式は、問合せ計画の作成時に評価できません。

- ネステッド・セル参照

- 参照モデルの参照

- ITERATION_NUMBER参照

このような式の結果を必要とするFORループを含むルールを問合せ計画の作成時に展開すると、エラーが発生します。問合せの実行時に展開する場合には、これらの式によってエラーが発生することはありません。

FORループ・コンストラクトに副問合せを含む式で、コンパイル時の展開が必要な場合、その副問合せは、展開を行えるようにするため、問合せ計画の作成時に評価されます。副問合せを問合せ計画の作成時に評価すると、カーソルを共有できなくなる場合があります。これは、同じ問合せであっても発行のたびに再コンパイルが必要になる可能性があることを意味します。このような式の展開を問合せの実行時まで遅延させると、コンパイル時の評価は不要になり、式がカーソルの共有性に影響を与えることもなくなります。

式のFORループに含まれる副問合せは、式が問合せの実行時に展開される場合には、WITH句内の表を参照できます。その式が問合せ計画の作成時に展開される場合は、エラーが発生します。

反復モデル

MODEL句のITERATEオプションを使用すると、ルールを特定の回数で反復的に評価できます。回数は、ITERATE句の引数として指定できます。ITERATEは、SEQUENTIAL ORDERモデルにのみ指定できます。このようなモデルは、反復モデルと呼ばれます。次の例を考えてみます。

SELECT x, s FROM DUAL
MODEL
DIMENSION BY (1 AS x) MEASURES (1024 AS s)
RULES UPDATE ITERATE (4)
(s[1] = s[1]/2);

表DUALにある行はただ1つです。したがって、このモデルでは、xでディメンション化され、メジャーsおよび単一の要素s[1] = 1024を持つ1次元配列を定義します。ルールs[1] = s[1]/2の評価は、4回繰り返されます。この問合せの結果は、列xと列sにそれぞれ値1と値64を持つ単一の行になります。ITERATE句の反復回数の引数には、正の整定数を指定する必要があります。オプションで早期終了条件を指定して、最大反復回数に達する前にルールの評価を停止することもできます。この条件は、ITERATEのUNTIL副次句で指定し、反復の終了時にチェックされます。したがって、ITERATEを指定した場合、最低1回は反復が行われます。ITERATE句の構文を次に示します。

ITERATE (number_of_iterations) [ UNTIL (condition) ]

反復評価は、指定の反復回数が終了した後または終了条件の評価がTRUEになった時点のいずれか早い時点で停止します。

場合によっては、反復の過程でのセル値の変更に基づいた終了条件が必要になることもあります。Oracle Databaseでは、このような条件を指定するためのメカニズムを備えています。この場合、UNTIL条件で現行の反復の前後におけるセルの値にアクセスできます。OracleのPREVIOUS関数は、引数にシングル・セル参照を取り、前の反復の後に存在するようになったセルのメジャー値を戻します。また、システム変数ITERATION_NUMBERを使用すると、現行の反復回数を取得できます。反復回数は、値0から開始し、1回の反復が終了するたびに増加します。PREVIOUSおよびITERATION_NUMBERを使用することにより、複雑な終了条件を作成できます。

次の反復モデルを考えてみます。この反復モデルでは、最大1000回までの継続的な反復の過程でs[1]の値が1未満に変化するまで、ルールを反復するよう指定しています。

SELECT x, s, iterations FROM DUAL
MODEL
DIMENSION BY (1 AS x) MEASURES (1024 AS s, 0 AS iterations)
RULES ITERATE (1000) UNTIL ABS(PREVIOUS(s[1]) - s[1]) < 1
(s[1] = s[1]/2, iterations[1] = ITERATION_NUMBER);

最新の値は正か負かわからない場合があるため、終了条件の指定には絶対値関数(ABS)が役立ちます。このモデルのルールは11回反復されます。これは、11回目の反復後にs[1]の値が0.5になるためです。この問合せの結果は、x、sおよび繰返し回数に対して、それぞれ1、0.5および10の値を持つ単一行になります。

PREVIOUS関数は、UNTIL条件でのみ使用できます。ただし、ITERATION_NUMBERは、メイン・モデルの任意の場所に指定できます。次の例では、ITERATION_NUMBERをセル参照で使用しています。

SELECT country, product, year, sales
FROM sales_view
MODEL
PARTITION BY (country) DIMENSION BY (product, year) MEASURES (sales sales)
IGNORE NAV
RULES ITERATE(3)
(sales['Bounce', 2002 + ITERATION_NUMBER] = sales['Bounce', 1999
+ ITERATION_NUMBER]);

この文では、Bounceの売上を配列1999-2001のセルから配列2002-2005にコピーします。

AUTOMATIC ORDERモデルでのルールの依存関係

Oracle Databaseでは、ルールの依存関係に基づいて、AUTOMATIC ORDERモデルでのルールの評価順序を決定します。ルールは、依存対象のルールが評価された後でのみ評価されます。ルールを評価するためのアルゴリズムは、依存関係分析およびモデルのルール間に循環依存があるかどうかに基づきます。循環依存には、「ルールAはルールBに依存し、ルールBはルールAに依存する」形式と「ルールがそのルール自身に依存する」自己循環の形式があります。前者の例を次に示します。

sales['Bounce', 2002] = 1.5 * sales['Y Box', 2002],
sales['Y Box', 2002] = 100000 / sales['Bounce', 2002

後者の例を次に示します。

sales['Bounce', 2002] = 25000 / sales['Bounce', 2002]

ただし、次のルールの場合、左辺と右辺で異なるメジャーにアクセスしているため、自己循環はありません。

projected_sales['Bounce', 2002] = 25000 / sales['Bounce', 2002]

Oracle Databaseでは、AUTOMATIC ORDERモデルの分析でルールに循環依存がないと判断した場合、依存関係の順序でルールを評価します。たとえば、次のAUTOMATIC ORDERモデルを考えてみます。

MODEL DIMENSION BY (prod, year) MEASURES (sale sales) IGNORE NAV
RULES AUTOMATIC ORDER
(sales['SUV', 2001] = 10000,
sales['Standard Mouse Pad', 2001] = sales['Finding Fido', 2001]
* 0.10 + sales['Boat', 2001] * 0.50,
sales['Boat', 2001] = sales['Finding Fido', 2001]
* 0.25 + sales['SUV', 2001]* 0.75,
sales['Finding Fido', 2001] = 20000)

ルール2はルール3と4に依存し、ルール3はルール1と4に依存し、ルール1と4はどのルールにも依存していません。この場合、Oracleではルールの依存関係が非循環であると判断して、有効な評価順序(1、4、3、2)または(4、1、3、2)のいずれかでルールを評価します。このタイプのルール評価は、ACYCLICアルゴリズムと呼ばれます。

Oracle Databaseでは、ルール間に循環依存がなくても、モデルが非循環であることを確認できない場合があります。これは、セル参照内に複雑な式がある場合などで発生します。このような場合、Oracle Databaseではルールが循環依存になっていると仮定し、CYCLICアルゴリズムを使用して、ルールおよびデータに基づいて反復的にモデルを評価します。収束に達した時点で反復は終了し、結果が戻されます。収束とは、これ以上モデルを実行しても、モデルのどのセルの値も変化しない状態のことをいいます。循環依存がない場合、必ず収束に達します。

AUTOMATIC ORDERモデルに循環依存のルールがある場合、Oracle Databaseでは前述のCYCLICアルゴリズムを使用します。アルゴリズムが試行される反復回数内で収束に達した場合、結果が生成されます。それ以外の場合は、循環検出エラーがレポートされます。この問題を回避するには、ルールを手動で順序付け、SEQUENTIAL ORDERを指定します。

順序付きルール

順序付きルールは、左辺にORDER BYが指定されているルールです。このルールは、ORDER BYで指定された順序でセルにアクセスし、右辺の計算を適用します。ルールの左辺にANYまたはシンボリック参照がある場合、ORDER BY句を指定しないと、ルールの結果はセルのアクセス順序に依存しているため不確定的であるという内容のエラーが戻される可能性があります。次のSEQUENTIAL ORDERモデルを考えてみます。

SELECT t, s
FROM sales, times
WHERE sales.time_id = times.time_id
GROUP BY calendar_year
MODEL
DIMENSION BY (calendar_year t) MEASURES (SUM(amount_sold) s)
RULES SEQUENTIAL ORDER
(s[ANY] = s[CV(t)-1]);

この問合せでは、すべての年tについて、ある年の売上高の値sを前年の売上高の値に設定するよう試みます。ただし、このルールの結果は、セルのアクセス順序に依存しています。年の昇順でセルにアクセスする場合、結果は表23-1の3列目に示す結果になります。降順でセルにアクセスする場合、結果は4列目に示す結果になります。

表23-1 順序付きルール

t

s

昇順の場合

降順の場合

1998

1210000982

NULL

NULL

1999

1473757581

NULL

1210000982

2000

2376222384

NULL

1473757581

2001

1267107764

NULL

2376222384

セルを降順で考慮する場合、表の4列目の結果を取得するには、次のように指定する必要があります。

SELECT t, s
FROM sales, times
WHERE sales.time_id = times.time_id
GROUP BY calendar_year
MODEL
DIMENSION BY (calendar_year t) MEASURES (SUM(amount_sold) s)
RULES SEQUENTIAL ORDER
(s[ANY] ORDER BY t DESC = s[CV(t)-1]);

一般的に、左辺のセル参照と一致するセル間で順序が一意となるかぎり、任意のORDER BY指定を使用できます。ルールのORDER BYの式には、定数、メジャー、ディメンション・キーを含めることができます。また、順序付けオプション[ASC | DESC] [NULLS FIRST | NULLS LAST]を指定すると、目的の順序を取得できます。

AUTOMATIC ORDERモデルのルールにORDER BYを指定することもできます。この場合、ルールの評価時に特定の順序でセルが考慮されます。ルールにORDER BYを指定した場合、そのルールが自己循環とみなされることはありません。たとえば、自己循環の式を使用した次のAUTOMATIC ORDERモデルを非循環にするとします。

MODEL
DIMENSION BY (calendar_year t) MEASURES (SUM(amount_sold) s)
RULES AUTOMATIC ORDER
(s[ANY] = s[CV(t)-1])

この場合、ORDER BYを使用して、アクセスする評価対象のセルの順序を指定する必要があります。次に例を示します。

s[ANY] ORDER BY t = s[CV(t) - 1]

これにより、Oracleは式の評価にACYCLICアルゴリズムを使用して、結果を確実に生成します。

分析関数

分析関数(ウィンドウ関数とも呼ばれる)は、ルールの右辺で使用できます。分析関数を使用すると、MODEL句で、より複雑な式を柔軟に計算できるようになります。次の例では、分析関数とMODEL句を組み合せて使用します。まず、GROUPING_ID関数を使用して集計の各レベルの識別子を計算するビューsales_rollup_timeを作成します。次に、quarterレベルとyearレベルの両方で売上高の累積合計を計算する問合せの中で、このビューを使用します。

CREATE OR REPLACE VIEW sales_rollup_time
AS
SELECT country_name country, calendar_year year, calendar_quarter_desc quarter,
GROUPING_ID(calendar_year, calendar_quarter_desc) gid, SUM(amount_sold) sale,
COUNT(amount_sold) cnt
FROM sales, times, customers, countries
WHERE sales.time_id = times.time_id AND sales.cust_id = customers.cust_id
AND customers.country_id = countries.country_id
GROUP BY country_name, calendar_year, ROLLUP(calendar_quarter_desc)
ORDER BY gid, country, year, quarter;

SELECT country, year, quarter, sale, csum
FROM sales_rollup_time
WHERE country IN ('United States of America', 'United Kingdom')
MODEL DIMENSION BY (country, year, quarter)
MEASURES (sale, gid, 0 csum)
(
csum[any, any, any] =
SUM(sale) OVER (PARTITION BY country, DECODE(gid,0,year,null)
ORDER BY year, quarter
ROWS UNBOUNDED PRECEDING)
)
ORDER BY country, gid, year, quarter;

COUNTRY                              YEAR QUARTER       SALE       CSUM
------------------------------ ---------- ------- ---------- ----------
United Kingdom                       1998 1998-01  484733.96  484733.96
United Kingdom                       1998 1998-02  386899.15  871633.11
United Kingdom                       1998 1998-03  402296.49  1273929.6
United Kingdom                       1998 1998-04  384747.94 1658677.54
United Kingdom                       1999 1999-01  394911.91  394911.91
United Kingdom                       1999 1999-02  331068.38  725980.29
United Kingdom                       1999 1999-03  383982.61  1109962.9
United Kingdom                       1999 1999-04  398147.59 1508110.49
United Kingdom                       2000 2000-01  424771.96  424771.96
United Kingdom                       2000 2000-02  351400.62  776172.58
United Kingdom                       2000 2000-03  385137.68 1161310.26
United Kingdom                       2000 2000-04   390912.8 1552223.06
United Kingdom                       2001 2001-01  343468.77  343468.77
United Kingdom                       2001 2001-02  415168.32  758637.09
United Kingdom                       2001 2001-03  478237.29 1236874.38
United Kingdom                       2001 2001-04  437877.47 1674751.85
United Kingdom                       1998         1658677.54 1658677.54
United Kingdom                       1999         1508110.49 3166788.03
United Kingdom                       2000         1552223.06 4719011.09
United Kingdom                       2001         1674751.85 6393762.94
...  /*and similar output for the US*/

分析関数を使用する際は、いくつかの固有の制限事項が適用されます。詳細は、「モデリング用SQLを使用する場合の規則および制限事項」を参照してください。

UNIQUE DIMENSIONとUNIQUE SINGLE REFERENCE

MODEL句のデフォルトの動作では、モデルへの入力で各行を一意に識別するために、PARTITION BYおよびDIMENSION BYキーが必要です。Oracleは、データの一意性を検証し、データが一意でない場合はエラーを戻します。PARTITION BYおよびDIMENSION BYキーでの入力行セットの一意性によって、シングル・セル参照がアクセスするモデル内のセルが1つに限定されることが保証されます。MODEL句でオプションのキーワードUNIQUE DIMENSIONを指定すると、この動作を明示できます。たとえば、次の問合せを考えてみます。

SELECT country, product, sales
FROM sales_view
WHERE country IN ('France', 'Poland')
MODEL UNIQUE DIMENSION
PARTITION BY (country) DIMENSION BY (product) MEASURES (sales sales)
IGNORE NAV RULES UPSERT
(sales['Bounce'] = sales['All Products'] * 0.24);

この問合せは、モデルに入力された行セットがcountryおよびproductでは一意でないため(yearも必要)、次の一意性違反エラーを戻します。

ERROR at line 2:ORA-32638: Non unique addressing in MODEL dimensions

ただし、次の問合せの場合、このようなエラーは戻されません。

SELECT country, product, year, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan')
MODEL UNIQUE DIMENSION
PARTITION BY (country) DIMENSION BY (product, year) MEASURES (sales sales)
RULES UPSERT
(sales['Bounce', 2003] = sales['All Products', 2002] * 0.24);

この場合、MODEL句への入力は、次に示すようにcountry、productおよびyearで一意です。

COUNTRY   PRODUCT                         YEAR   SALES
-------   -----------------------------   ----   --------
Italy     1.44MB External 3.5" Diskette   1998    3141.84
Italy     1.44MB External 3.5" Diskette   1999    3086.87
Italy     1.44MB External 3.5" Diskette   2000    3440.37
Italy     1.44MB External 3.5" Diskette   2001     855.23
...

UNIQUE SINGLE REFERENCEキーワードを指定すると、一意性を簡単にチェックできます。これにより、処理時間を節約できます。この場合、MODEL句は、ルールの右辺に現れるシングル・セル参照のみの一意性をチェックします。したがって、一意性違反エラーを戻した問合せでUNIQUE DIMENSIONのかわりにUNIQUE SINGLE REFERENCEを指定すると、エラーが発生しなくなります。

UNIQUE DIMENSIONとUNIQUE SINGLE REFERENCEのもう1つの違いは、左辺にシングル・セル参照があるルールで更新できるセルの数です。UNIQUE DIMENSIONの場合、左辺のシングル・セル参照に1つのセルのみが一致するようなルールで更新できる行は最大でも1つです。これは、入力行セットがPARTITION BYおよびDIMENSION BYキーで一意であるためです。UNIQUE SINGLE REFERENCEの場合、左辺のシングル・セル参照に一致するすべてのセルがルールで更新されます。

モデリング用SQLを使用する場合の規則および制限事項

MODEL句を使用する場合は、次の一般規則および制限事項が適用されます。

- 更新可能な列は、メインSQLモデルのMEASURES副次句で指定された列のみです。参照モデルのメジャーは更新できません。

- MODEL句が評価されるのは、SELECT DISTINCT句を除く問合せブロック内のすべての句およびORDER BY句が評価された後です。SELECT構文のリスト内の句および式は、MODEL句の後に評価されます。

- 問合せにMODEL句がある場合、問合せのSELECTおよびORDER BYリストに集計関数または分析関数は指定できません。これらの関数が必要な場合は、PARTITION BY、DIMENSION BYおよびMEASURESリストで指定可能であり、別名化する必要があります。これにより、SELECTまたはORDER BY句で別名を使用できるようになります。次の例では、分析関数RANKをMODEL句のMEASURESリストで指定および別名化し、その別名をSELECT構文のリストで使用して、外部の問合せがランクに基づいて結果の行を順序付けできるようにしています。

SELECT country, product, year, s, RNK
FROM (SELECT country, product, year, s, rnk
FROM sales_view
MODEL
PARTITION BY (country) DIMENSION BY (product, year)
MEASURES (sales s, year y, RANK() OVER (ORDER BY sales) rnk)
RULES UPSERT
(s['Bounce Increase 90-99', 2001] =
REGR_SLOPE(s, y) ['Bounce', year BETWEEN 1990 AND 2000],
s['Bounce', 2001] = s['Bounce', 2000] *
(1+s['Bounce increase 90-99', 2001])))
WHERE product <> 'Bounce Increase 90-99'
ORDER BY country, year, rnk, product;

- ルールの右辺にマルチセル参照がある場合、集計関数を適用して、マルチセル参照の複数のメジャー値を単一値に変換する必要があります。これには、通常の集計関数、分析集計関数(逆パーセンタイル関数、仮説ランク関数、仮説分布関数)、ユーザー定義の集計関数など、任意の種類の集計関数を使用できます。

- UPSERTを指定できるのは、左辺に位置ベースのシングル・セル参照があるルールのみです。それ以外のルールはすべて、UPSERTオプションを指定した場合でも、UPDATEになります。

- FORループでは、負の値の増加は禁止されています。また、空のFORループも禁止されています。たとえば、FOR d FROM 2005 TO 2001 INCREMENT -1は無効です。かわりに、FOR d FROM 2005 TO 2001 DECREMENT 1を使用する必要があります。FOR d FROM 2005 TO 2001 INCREMENT 1は、空のループを指定しているため無効です。

- FORコンストラクト内を除き、ルールではネストした問合せ式(副問合せ)は使用できません。たとえば、次の文を発行した場合は無効になります。

SELECT *
FROM sales_view WHERE country = 'Poland'
MODEL DIMENSION BY (product, year)
MEASURES (sales sales)
RULES UPSERT
(sales['Bounce', 2003] = sales['Bounce', 2002] +
(SELECT SUM(sales) FROM sales_view));

これは、ルールの右辺に副問合せがあるためです。前述の問合せは、次の有効な方法で記述しなおすことができます。

SELECT *
FROM sales_view WHERE country = 'Poland'
MODEL DIMENSION BY (product, year)
MEASURES (sales sales, (SELECT SUM(sales) FROM sales_view) AS grand_total)
RULES UPSERT
(sales['Bounce', 2003] =sales['Bounce', 2002] +
grand_total['Bounce', 2002]);

- ルールの左辺に指定されたFORコンストラクトにも副問合せを使用できます。ただし、次の制限があります。

- 相関させることはできません。

- 戻り値の行数は10,000行未満に制限する必要があります。

- WITH句で定義した問合せにすることはできません。

- カーソルは共有できません。

ネステッド・セル参照には、次の制限事項があります。

- ネステッド・セル参照は、シングル・セル参照である必要があります。ネステッド・セル参照での集計はサポートされていません。したがって、s['Bounce', MAX(best_year)['Bounce', ANY]]のような指定は無効になります。

- メイン・モデルのネステッド・セル参照の場合、サポートされているネストのレベルは1レベルのみです。たとえば、s['Bounce', best_year['Bounce', 2001]]は有効ですが、s['Bounce', best_year['Bounce', best_year['Bounce', 2001]]]は無効になります。

- AUTOMATIC ORDERモデルでは、ルールの左辺にあるネステッド・セル参照をモデルのルールで更新しないようにしてください。この制限によって、参照メジャーの更新が原因でルールの依存関係が無原則に変更される(そのため、結果が不確定的になる)のを防止できます。
SEQUENTIAL ORDERモデルのネステッド・セル参照の場合、このような制限はありません。また、SEQUENTIALまたはAUTOMATIC ORDERのどちらのモデルでも、ルールの右辺にあるネステッド・セル参照には、この制限は適用されません。

参照モデルには、次の制限事項があります。

- 参照モデルを定義する問合せは、外部の問合せと相関させることはできません。ただし、副問合せやビューなどを持つ問合せにすることはできます。

- 参照モデルでは、PARTITION BY句は指定できません。

- 参照モデルは更新できません。

ウィンドウ関数には、次の制限事項があります。

- OVER句に指定できる式は、定数の式、メジャー、MODEL句のPARTITION BYとDIMENSION BYのキー、および単一のセルの式です。集計は、OVER句の内部には指定できません。次の式は許容されます。

rnk[ANY, ANY, ANY] = RANK() (PARTITION BY prod, country ORDER BY sale)

次の式は許容されません。

rnk[ANY, ANY, ANY] = RANK() (PARTITION BY prod, country ORDER BY SUM(sale))

- ルールの右辺にウィンドウ関数が含まれる場合、同じルールの左辺にはORDER BY句を指定できません。

- ウィンドウ関数と集計関数の両方を同じルールの右辺には指定できません。

- ウィンドウ関数は、UPDATEルールの右辺でのみ使用できます。

- ルールの左辺にFORループがある場合、ウィンドウ関数は同じルールの右辺で使用できません。

SQLモデリングのパフォーマンスに関する考慮事項

以降の項では、MODEL句を使用する場合のパフォーマンスに影響するトピックについて説明します。

- パラレル実行

- 集計計算

- EXPLAIN PLANを使用したモデル問合せの理解

パラレル実行

MODEL句の計算のスケーラビリティは、使用するプロセッサの数に応じて向上します。また、スケーラビリティは、PARTITION BY句で定義したパーティション間でMODEL計算をパラレルに実行することにより実現します。データは、PARTITION BYキーの値に基づいて(パラレル問合せスレーブと呼ばれる)処理要素に分散されます。これにより、PARTITION BYキーの値が同じ行は、すべて同じスレーブに配置されます。この場合、パーティションの内部処理では、論理的に処理されるパーティションと内部的に処理されるパーティションは1対1の一致とはなりません。これにより、各スレーブは、他のスレーブとは関係なくMODEL句の計算を終了できます。データは、ハッシュ・ベースまたはレンジ・ベースでパーティション化できます。次のMODEL句を考えてみます。

MODEL
PARTITION BY (country) DIMENSION BY (product, time) MEASURES (sales)
RULES UPDATE
(sales['Bounce', 2002] = 1.2 * sales['Bounce', 2001],
sales['Car', 2002] = 0.8 * sales['Car', 2001])

ここで、入力データは、PARTITION BYキーcountryに基づいてスレーブ間でパーティション化されます。このパーティション化は、ハッシュ・ベースまたはレンジ・ベースで行うことができます。各スレーブは、受け取ったデータのルールを評価します。

モデル計算のパラレル化は、MODEL句の指定方法によって制御または制限されます。MODEL句にPARTITION BYキーがない場合、(後述の例外を除き)計算はパラレル化できません。PARTITION BYキーのカーディナリティが非常に低い場合、並列度は制限されます。このような場合、Oracleでは、パーティション化に使用可能なDIMENSION BYキーを識別します。たとえば、前述の例とほぼ同等だがPARTITION BYキーがない、次のようなMODEL句があるとします。

MODEL
DIMENSION BY (country, product, time) MEASURES (sales)
RULES UPDATE
(sales[ANY, 'Bounce', 2002] = 1.2 * sales[CV(country), 'Bounce', 2001],
sales[ANY, 'Car', 2002] = 0.8 * sales[CV(country), 'Car', 2001])

この場合、Oracle Databaseでは、DIMENSION BYキーcountryがパーティション化に使用可能であると識別し、内部のパーティション化の処理にregionを使用します。データは、countryのスレーブ間でパーティション化され、パラレル実行が行われます。

集計計算

MODEL句の集計の処理には2通りの方法があります。1つはパーティション内のデータをスキャンして集計する一般的な方法、もう1つはウィンドウ形式の集計を行う効率的な方法です。最初の方法の例を次に示します。ここでは、新しいディメンション・メンバーALL_2002_productsを導入し、その値が全製品の2002年の合計売上になるよう計算します。

MODEL PARTITION BY (country) DIMENSION BY (product, time) MEASURES (sale sales)
RULES UPSERT
(sales['ALL_2002_products', 2002] = SUM(sales)[ANY, 2002])

この場合、集計の合計を評価するために、各パーティションをスキャンして、全製品の2002年のセルを検索して集計します。ルールの左辺が複数のセルを参照する場合は、左辺で参照されるセルごとにパーティションがスキャンされ、右辺の集計が計算されます。たとえば、次の例を考えてみます。

MODEL PARTITION BY (country) DIMENSION BY (product, time)
MEASURES (sale sales, 0 avg_exclusive)
RULES UPDATE
(avg_exclusive[ANY, 2002] =  AVG(sales)[product <> CV(product), CV(time)])

このルールは、2002年の全製品を対象にメジャーavg_exclusiveを計算します。メジャーavg_exclusiveは、現行の製品を除外したすべての製品の平均売上として定義されています。この場合、2002年の全製品についてパーティションのデータがスキャンされ集計が計算されるため、コストが高くなる可能性があります。

Oracle Databaseでは、このような集計の評価を最適化するために、集計関数で使用される、ウィンドウ形式の計算のシナリオが複数用意されています。これらのシナリオでは、左辺にマルチセル参照を持つルールを指定し、移動平均や累積合計などのウィンドウ計算を実行します。次の例を考えてみます。

MODEL PARTITION BY (country) DIMENSION BY (product, time)
MEASURES (sale sales, 0 mavg)
RULES UPDATE
(mavg[product IN ('Bounce', 'Y Box', 'Mouse Pad'), ANY] =
AVG(sales)[CV(product), time BETWEEN CV(time)
AND CV(time) - 2])

この例では、3年間にわたる製品Bounce、Y BoxおよびMouse Padの売上の移動平均を計算します。左辺で参照されるすべてのセルについてパーティションがスキャンされると、集計の評価が非常に非効率的になります。Oracleでは、ウィンドウ形式で計算が識別され、効率的に評価されます。この場合、productおよびtimeの入力をソートしてから、データを1回スキャンして移動平均を計算します。このルールは、次のように製品Bounce、Y BoxおよびMouse Padの売上データに適用する集計関数として表すことができます。

AVG(sales) OVER (PARTITION BY product ORDER BY time
RANGE BETWEEN 2 PRECEDING AND CURRENT ROW)

この計算形式は、WINDOW (IN MODEL) SORTと呼ばれます。この形式の集計を適用できるのは、ルールの左辺にORDER BYが指定されていないマルチセル参照があり、ルールの右辺に単純な集計(SUM、COUNT、MIN、MAX、STDEV、VAR)があり、右辺のいずれか1つのディメンションにブール型の述語(<、<=、>、>=、BETWEEN)があり、右辺のその他のディメンションがすべてCVで修飾されている場合です。

EXPLAIN PLANを使用したモデル問合せの理解

OracleのEXPLAIN PLAN機能は、モデルに完全対応しています。問合せに対するEXPLAIN PLANのメインの出力では、モデルおよび使用されたアルゴリズムを示す行が表示されます。参照モデルの場合、EXPLAIN PLANの出力ではキーワードREFERENCEでタグ付けられます。また、いずれかのルールでウィンドウ形式の集計計算を許可する場合、EXPLAIN PLANにWINDOW (IN MODEL) SORTという注釈が付けられます。

EXPLAIN PLANを調べることにより、モデルの評価で使用されたアルゴリズムを確認できます。モデルにSEQUENTIAL ORDERが指定されている場合、ORDEREDが表示されます。AUTOMATIC ORDERモデルの場合は、評価用のアルゴリズムがACYCLICかCYCLICかに応じて、ACYCLICまたはCYCLICが表示されます。さらに、左辺のすべてのセル参照がシングル・セル参照であり、ルールの右辺の集計が単純で非個別的な算術集計(SUM、COUNT、AVGなど)の場合、アルゴリズムがORDEREDおよびACYCLICだと、EXPLAIN PLANの出力に注釈FASTが付けられます。この場合、ルールの評価が非常に効率的になるため、注釈FASTが付きます。したがって、EXPLAIN PLANの出力では、MODEL {ORDERED [FAST] | ACYCLIC [FAST] | CYCLIC}と表示されます。

ORDERED FASTの使用例

このモデルでは、ルールの左辺にシングル・セル参照のみがあり、最初のルールの右辺にある集計AVGは単純で非個別的な集計になっています。

EXPLAIN PLAN FOR
SELECT country, prod, year, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan')
MODEL UNIQUE DIMENSION
PARTITION BY (country) DIMENSION BY (prod, year) MEASURES (sale sales)
RULES UPSERT
(sales['Bounce', 2003] = AVG(sales)[ANY, 2002] * 1.24,
sales['Y Box', 2003] = sales['Bounce', 2003] * 0.25);

ORDEREDの使用例

次の例の場合、2番目のルールの左辺がマルチセル参照になっているため、FASTは選択されません。

EXPLAIN PLAN FOR
SELECT country, prod, year, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan')
MODEL UNIQUE DIMENSION
PARTITION BY (country) DIMENSION BY (prod, year) MEASURES (sale sales)
RULES UPSERT
(sales['Bounce', 2003] = AVG(sales)[ANY, 2002] * 1.24,
sales[prod <> 'Bounce', 2003] = sales['Bounce', 2003] * 0.25);

ACYCLIC FASTの使用例

このモデルのルールは非循環になっているため、EXPLAIN PLANではACYCLICが表示されます。この場合、FASTも選択されます。

EXPLAIN PLAN FOR
SELECT country, prod, year, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan')
MODEL UNIQUE DIMENSION
PARTITION BY (country) DIMENSION BY (prod, year) MEASURES (sale sales)
RULES UPSERT AUTOMATIC ORDER
(sales['Y Box', 2003] = sales['Bounce', 2003] * 0.25,
sales['Bounce', 2003] = sales['Bounce', 2002] / SUM(sales)[ANY, 2002] * 2 *
sales['All Products', 2003],
sales['All Products', 2003] = 200000);

ACYCLICの使用例

このモデルのルールは非循環です。2番目のルールでは、2002年の売上高の中央値を示すPERCENTILE_DISC集計があります。この集計は、単純な集計関数ではありません。そのため、FASTは選択されず、EXPLAIN PLANではACYCLICのみが表示されます。

SELECT country, prod, year, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan')
MODEL UNIQUE DIMENSION
PARTITION BY (country) DIMENSION BY (prod, year) MEASURES (sale sales)
RULES UPSERT AUTOMATIC ORDER
(sales['Y Box', 2003] = sales['Bounce', 2003] * 0.25,
sales['Bounce',2003] = PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY
sales)[ANY,2002] / SUM(sales)[ANY, 2002] * 2 * sales['All Products', 2003],
sales['All Products', 2003] = 200000);

CYCLICの使用例

2番目と3番目のルールは循環依存になっているため、このモデルではCYCLICアルゴリズムが選択されます。

EXPLAIN PLAN FOR
SELECT country, prod, year, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan')
MODEL UNIQUE DIMENSION
PARTITION BY (country) DIMENSION BY (prod, year) MEASURES (sale sales)
IGNORE NAV RULES UPSERT AUTOMATIC ORDER
(sales['All Products', 2003] = 200000,
sales['Y Box', 2003] = sales['Bounce', 2003] * 0.25,
sales['Bounce', 2003] = sales['Y Box', 2003] +
(sales['Bounce', 2002] / SUM(sales)[ANY, 2002] * 2 *
sales['All Products', 2003]));

SQLモデリングの例

この項の各例では、sales_viewに加え、次のビューが定義されていると想定します。このビューは、製品別および国別の月単位による合計売上高と販売数量を示します。

CREATE VIEW sales_view2 AS
SELECT country_name country, prod_name product, calendar_year year,
calendar_month_name month, SUM(amount_sold) sale, COUNT(amount_sold) cnt
FROM sales, times, customers, countries, products
WHERE sales.time_id = times.time_id AND
sales.prod_id = products.prod_id AND
sales.cust_id = customers.cust_id AND
customers.country_id = countries.country_id
GROUP BY country_name, prod_name, calendar_year, calendar_month_name;

例1   売上高の差の計算

ItalyとSpainの売上高および両国の売上高の差を製品別に示します。差は、country = 'Diff Italy-Spain'を使用して新しい行に配置されます。

SELECT product, country, sales
FROM sales_view
WHERE country IN ('Italy', 'Spain')
GROUP BY product, country
MODEL
PARTITION BY (product) DIMENSION BY (country) MEASURES (SUM(sales) AS sales)
RULES UPSERT
(sales['DIFF ITALY-SPAIN'] = sales['Italy'] - sales['Spain']);

例2   変化率の計算

2000年10月から11月と同年11月から12月のそれぞれの期間において、各国での各製品の売上高が同じ月間変化率で増加(または減少)した場合、会社全体および国ごとの第4四半期の売上高を計算します。

SELECT country, SUM(sales)
FROM (SELECT product, country, month, sales
FROM sales_view2
WHERE year=2000 AND month IN ('October','November')
MODEL
PARTITION BY (product, country) DIMENSION BY (month) MEASURES (sale sales)
RULES
(sales['December']=(sales['November'] /sales['October']) *sales['November']))
GROUP BY GROUPING SETS ((),(country));

例3   正味現在価値の計算

一連の定期的なキャッシュ・フローの正味現在価値(NPV)を計算するとします。シナリオには、2つのプロジェクトがあります。それぞれ、時間0での負のキャッシュ・フローで表される初期投資から開始します。初期投資以降の3年間のキャッシュ・フローは正の値になります。最初に、次の文で表(cash_flow)を作成し、データを移入します。

CREATE TABLE cash_flow (year DATE, i INTEGER, prod VARCHAR2(3), amount NUMBER);
INSERT INTO cash_flow VALUES (TO_DATE('1999', 'YYYY'),  0,  'vcr',  -100.00);
INSERT INTO cash_flow VALUES (TO_DATE('2000', 'YYYY'),  1,  'vcr',   12.00);
INSERT INTO cash_flow VALUES (TO_DATE('2001', 'YYYY'),  2,  'vcr',  10.00);
INSERT INTO cash_flow VALUES (TO_DATE('2002', 'YYYY'),  3,  'vcr',  20.00);
INSERT INTO cash_flow VALUES (TO_DATE('1999', 'YYYY'),  0,  'dvd',  -200.00);
INSERT INTO cash_flow VALUES (TO_DATE('2000', 'YYYY'),  1,  'dvd',  22.00);
INSERT INTO cash_flow VALUES (TO_DATE('2001', 'YYYY'),  2,  'dvd',  12.00);
INSERT INTO cash_flow VALUES (TO_DATE('2002', 'YYYY'),  3,  'dvd',  14.00);

割引率を0.14としてNPVを計算する場合、次の文を発行します。

SELECT year, i, prod, amount, npv
FROM cash_flow
MODEL PARTITION BY (prod)
DIMENSION BY (i)
MEASURES (amount, 0 npv, year)
RULES
(npv[0] = amount[0],
npv[i !=0] ORDER BY i =
amount[CV()]/ POWER(1.14,CV(i)) + npv[CV(i)-1]);

YEAR               I PRO     AMOUNT        NPV
--------- ---------- --- ---------- ----------
01-AUG-99          0 dvd       -200       -200
01-AUG-00          1 dvd         22 -180.70175
01-AUG-01          2 dvd         12 -171.46814
01-AUG-02          3 dvd         14 -162.01854
01-AUG-99          0 vcr       -100       -100
01-AUG-00          1 vcr         12 -89.473684
01-AUG-01          2 vcr         10 -81.779009
01-AUG-02          3 vcr         20 -68.279579

例4   連立方程式を使用した計算

利子負担率を純所得(純所得=給与-税金-利子)の30%にするとします。利子は総所得から控除できます。税率は給与の38%、資産売却益の28%です。給与は100,000ドル、資産売却益は15,000ドルとします。純所得、税額および利子負担率は不明です。純所得は利子に依存し、利子は純所得に依存していることから、これは連立方程式であるため、ITERATE句が含まれていることに注目してください。

最初に、表ledgerを作成します。

CREATE TABLE  ledger  (account  VARCHAR2(20), balance  NUMBER(10,2) );

次の5つの行を挿入します。

INSERT INTO ledger VALUES  ('Salary', 100000);
INSERT INTO ledger VALUES  ('Capital_gains', 15000);
INSERT INTO ledger VALUES  ('Net', 0);
INSERT INTO ledger VALUES  ('Tax', 0);
INSERT INTO ledger VALUES  ('Interest', 0);

続いて、次の文を発行します。

SELECT s, account
FROM ledger
MODEL
DIMENSION  BY (account) MEASURES (balance s)
RULES  ITERATE (100)
(s['Net']=s['Salary']-s['Interest']-s['Tax'],
s['Tax']=(s['Salary']-s['Interest'])*0.38 + s['Capital_gains']*0.28,
s['Interest']=s['Net']*0.30);

出力は次のようになります(端数は切り捨てられています)。

S ACCOUNT
---------- --------------------
100000 Salary
15000 Capital_gains
48735.2445 Net
36644.1821 Tax
14620.5734 Interest

例5   回帰を使用した計算

2001年のBounceの売上高は、過去3年(1998年から2000年)と同様、2000年と比べて増加する見通しです。この増加額を計算するには、次のように回帰関数REGR_SLOPEを使用します。ここでは、次の期間の値を計算するため、傾きを2000年の値に追加すれば十分です。

SELECT * FROM
(SELECT country, product, year, projected_sale, sales
FROM sales_view
WHERE country IN ('Italy', 'Japan') AND product IN ('Bounce')
MODEL
PARTITION BY (country) DIMENSION BY (product, year)
MEASURES (sales sales, year y, CAST(NULL AS NUMBER) projected_sale) IGNORE NAV
RULES UPSERT
(projected_sale[FOR product IN ('Bounce'), 2001] =
sales[CV(), 2000] +
REGR_SLOPE(sales, y)[CV(), year BETWEEN 1998 AND 2000]))
ORDER BY country, product, year;

出力は次のようになります。

COUNTRY    PRODUCT           YEAR   PROJECTED_SALE  SALES
-------    -------           ----   --------------  -------
Italy      Bounce            1999                    2474.78
Italy      Bounce            2000                    4333.69
Italy      Bounce            2001           6192.6   4846.3
Japan      Bounce            1999                    2961.3
Japan      Bounce            2000                    5133.53
Japan      Bounce            2001          7305.76   6303.6

例6   貸付金の割賦償還額の計算

この例では、貸付金のファクト表から選択した住宅ローンに関する情報を使用して、任意の数の顧客を対象とした貸付金の割賦償還額の表を作成します。最初に、次の2つの表を作成し、必要なデータを挿入します。

- mortgage_facts
顧客名など個々の顧客のローンに関する情報、行に格納されているローンに関するファクト、およびそのファクトの値を保持します。この例で格納されるファクトには、ローン(Loan)、年間利子率(Annual_Interest)およびローンの支払回数(Payments)があります。また、2名の顧客(SmithとJones)の値も挿入されます。

CREATE TABLE mortgage_facts (customer VARCHAR2(20), fact VARCHAR2(20),
amount  NUMBER(10,2));
INSERT INTO mortgage_facts  VALUES ('Smith', 'Loan', 100000);
INSERT INTO mortgage_facts  VALUES ('Smith', 'Annual_Interest', 12);
INSERT INTO mortgage_facts  VALUES ('Smith', 'Payments', 360);
INSERT INTO mortgage_facts  VALUES ('Smith', 'Payment', 0);
INSERT INTO mortgage_facts  VALUES ('Jones', 'Loan', 200000);
INSERT INTO mortgage_facts  VALUES ('Jones', 'Annual_Interest', 12);
INSERT INTO mortgage_facts  VALUES ('Jones', 'Payments', 180);
INSERT INTO mortgage_facts  VALUES ('Jones', 'Payment', 0);

- mortgage
計算の出力情報を保持します。これには、顧客、支払回数(pmt_num)、支払に適用される元本金額(principalp)、支払に適用される利子(interestp)、ローンの残高(mort_balance)の列があります。新しいセルをパーティションにアップサートするには、事前に各パーティションに少なくとも1つの行を作成しておく必要があります。したがって、2名の顧客が支払を行う前に、顧客の値を貸付金の表に入力します。このシード情報は、表mortgage_factsに基づいてSQLのINSERT文を使用すると、簡単に生成できます。

CREATE TABLE mortgage_facts (customer VARCHAR2(20), fact VARCHAR2(20),
amount  NUMBER(10,2));

INSERT INTO mortgage_facts  VALUES ('Smith', 'Loan', 100000);
INSERT INTO mortgage_facts  VALUES ('Smith', 'Annual_Interest', 12);
INSERT INTO mortgage_facts  VALUES ('Smith', 'Payments', 360);
INSERT INTO mortgage_facts  VALUES ('Smith', 'Payment', 0);
INSERT INTO mortgage_facts  VALUES ('Jones', 'Loan', 200000);
INSERT INTO mortgage_facts  VALUES ('Jones', 'Annual_Interest', 12);
INSERT INTO mortgage_facts  VALUES ('Jones', 'Payments', 180);
INSERT INTO mortgage_facts  VALUES ('Jones', 'Payment', 0);

CREATE TABLE mortgage (customer VARCHAR2(20), pmt_num NUMBER(4),
principalp NUMBER(10,2), interestp NUMBER(10,2), mort_balance NUMBER(10,2));

INSERT INTO mortgage VALUES ('Jones',0, 0, 0, 200000);
INSERT INTO mortgage VALUES ('Smith',0, 0, 0, 100000);

次のSQL文は複雑であるため、必要に応じて個々の行に注釈を付けています。これらの行の詳細については後述します。

SELECT c, p, m, pp, ip
FROM MORTGAGE
MODEL                                                                  --See 1
REFERENCE R ON
(SELECT customer, fact, amt                                         --See 2
FROM mortgage_facts
MODEL DIMENSION BY (customer, fact) MEASURES (amount amt)          --See 3
RULES
(amt[any, 'PaymentAmt']= (amt[CV(),'Loan']*
Power(1+ (amt[CV(),'Annual_Interest']/100/12),
amt[CV(),'Payments']) *
(amt[CV(),'Annual_Interest']/100/12)) /
(Power(1+(amt[CV(),'Annual_Interest']/100/12),
amt[CV(),'Payments']) - 1)
)
)
DIMENSION BY (customer cust, fact) measures (amt)                     --See 4
MAIN amortization
PARTITION BY (customer c)                                             --See 5
DIMENSION BY (0 p)                                                    --See 6
MEASURES (principalp pp, interestp ip, mort_balance m, customer mc)   --See 7
RULES
ITERATE(1000) UNTIL (ITERATION_NUMBER+1 =
r.amt[mc[0],'Payments'])                                                --See 8
(ip[ITERATION_NUMBER+1] = m[CV()-1] *
r.amt[mc[0], 'Annual_Interest']/1200,                            --See 9
pp[ITERATION_NUMBER+1] = r.amt[mc[0], 'PaymentAmt'] - ip[CV()],    --See 10
m[ITERATION_NUMBER+1] = m[CV()-1] - pp[CV()]                      --See 11
)
ORDER BY c, p;

次の番号は、例で示した番号と対応しています。

1: これは、メイン・モデル定義の開始です。

2から4: これらの行では、参照モデルRの開始と終了をマークしています。このモデルは、各顧客のローンについて月々の支払額を計算するSELECT文を定義します。このSELECT文では、3のラベルが付けられた行で開始し、ルールが1つ指定された独自のMODEL句を使用します。このルールでは、表mortgage_factsの情報に基づいてamtの値を定義します。4のラベルが付けられた行で定義した、顧客名custおよびファクトの値factによってディメンション化されている、参照モデルRによって戻されるメジャーはamtです。

参照モデルが計算されると、メイン・モデルでは、その値を使用して他の計算を実行します。参照モデルが計算されると、メイン・モデルでは、その値を使用して他の計算を実行します。参照モデルRは、mortgage_factの既存の各行の1行を戻し、さらに顧客ごとに新規に計算された行を戻します。この場合、ファクト・タイプはPaymentでamtが月々の支払額です。Rの出力から特定の金額を使用する場合は、式r.amt[<customer_name>,<fact_name>]で指定します。

5: これは、メイン・モデル定義の続きです。cに別名化したcustomerで出力をパーティション化します。

6: メイン・モデルを定数値0でディメンション化し、pに別名化します。これは、行の支払回数を表します。

7: メジャーを4つ定義しています。principalp (pp)は月々のローンに適用される元本金額、interestp (ip)は月々に支払われる利子、mort_balance (m)はローンの支払後における貸付金の残高値を示します。customer (mc)はパーティション化をサポートするために使用されます。

8: ルールのブロックを開始します。ルールの計算は、最大1000回実行されます。計算は顧客ごとに各月につき1回実行されるため、ローンに指定できる最大の月数は1000です。反復処理は、ITERATION_NUMBER+1が参照Rから導出される支払額に等しくなった時点で停止します。参照Rの値は、REFERENCE句で定義したamt(金額)メジャーであることに注意してください。この参照値は、r.amt[<customer_name>,<fact>]として処理されます。反復行で使用される式"r.amt[mc[0], 'Payments']"は、参照Rからの金額に解決し、この場合、顧客名はmc[0]で解決される値です。各パーティションに含まれる顧客は1つのみのため、mc[0]が持つことができる値は1つのみです。これにより、"r.amt[mc[0], 'Payments']"から、REFERENCE句の現行の顧客に関する支払回数の値が得られます。つまり、顧客の支払回数と同じ回数でルールが実行されます。

9から11: このブロックの最初の2つのルールは、8で説明したr.amt参照と同じタイプです。違いは、ルールipがファクトの値をAnnual_Interestとして定義している点です。各ルールは、他のいずれかのメジャー値を参照することに注意してください。各ルールの左辺で使用されている式"[ITERATION_NUMBER+1]"によって、新しいディメンション値が作成され、メジャーが結果セットにアップサートされます。これにより、各顧客のすべての支払について月々の割賦償還額を示す行が結果に含まれるようになります。

この例の最終行によって、顧客別およびローンの支払回数別に結果がソートされます。

24 高度なビジネス・インテリジェンス問合せ

この章では、高度なビジネス・インテリジェンス問合せを実行するためのテクニックについて説明します。この章は、様々なSQL機能を組み合せて複雑な分析を実行する方法をより深く理解することを目的としています。この章で扱う機能は、第21章「データ・ウェアハウスにおける集計のためのSQL」、第22章「分析計算およびレポート用SQL関数」および第23章「モデリングのSQL」でも個別に説明していますが、各機能を個々に見ていくだけでは、それらを組み合せて使用する方法を十分に理解することはできません。ここでは、各機能を組み合せることによって、どのような分析が可能になるかを示します。

「高度な」ビジネス・インテリジェンス問合せとは一体何でしょうか。「高度な」という形容詞が最もふさわしいのは、複数のディメンション階層を伴うことの多い複数手順の問合せです。複数手順の問合せでは、最終的な結果は、複数セットの取得データおよび複数の計算手順によって導かれ、取得されるデータには、ディメンション階層の複数のレベルが含まれる場合があります。高度な問合せの主な例としては、複数の条件に基づく市場シェアの計算や、データのギャップ補完を必要とする売上予測があげられます。

この章では、ネストしたインライン・ビュー、CASE式、パーティション外部結合、MODEL句、WITH句、分析SQL関数などの使用方法を、例を示しながら説明します。必要に応じて、問合せ計画についても説明します。この章の内容は次のとおりです。

- ビジネス・インテリジェンス問合せの例

ビジネス・インテリジェンス問合せの例

この章の問合せは、様々なビジネス・インテリジェンス・タスクを示しています。これらの問合せのトピックおよび各問合せで使用されている機能は次のとおりです。

- 複雑な複数手順の条件に基づく市場シェアの変化率。ネストしたインライン・ビュー、CASE式および分析SQL関数の使用方法を示します。

- データのギャップ補完を伴う売上予測。MODEL句をパーティション外部結合およびCASE式と組み合せて使用する方法を示します。

- 購入額別のバケットに顧客をグループ化する顧客分析。WITH句(問合せの副ファクタリング)と、分析SQL関数percentile_contおよびwidth_bucketの使用方法を示します。

- 項目セットにグループ化する顧客項目。DBMS_FREQUENT_ITEMSET.FI_TRANSACTIONALをテーブル・ファンクションとして使用する高頻度項目セットの計算方法を示します。

例1   計算セット内での製品の市場シェアの変化率

この例では、収益が20%以上増加したアカウントについて、現在の3か月期間における売上上位20%を占める製品グループの市場シェアの、前年の同期間との変化率を求めます。

ここでは、市場シェアを「総売上高に占める当該製品の売上高の割合」と定義します。このように定義するのは、shサンプル・スキーマに競合企業のデータがなく、自社製品と競合企業製品の売上を比較する通常のシェア計算を行うことができないためです。ただし、ここでのシェア計算に必要な作業は、競合的な市場シェアの計算と論理的にはそれほど変わりません。

次に、この問合せで取得する情報を示します。これらの情報は、この順序で取得する必要があります。

1. 指定した3か月期間の間に、前年の同期間と比較して購入額が20%以上増加した都市。都市は1か国に限定され、売上に宣伝は関与していないことに注意してください。

2. 前の手順で求めた都市グループにおける、売上上位20%の製品。つまり、この顧客グループ全体での製品別総売上高を求め、最も売上の多い製品20%を選択します。

3. 前の手順で求めた各製品の売上高の割合。つまり、前の手順で求めた製品グループを使用して、全製品の総売上高に占める各製品の売上高の割合を求めます。前年の同期間における割合を求め、2つの年における割合の変化率を計算します。

この例で使用しているテクニックは次のとおりです。

- この問合せは、WITH句およびネストしたインライン・ビューを使用して実行されます。各インライン・ビューにはそのデータ要素の内容を示す別名が付けられており、各インライン・ビューの境界はコメント行で示されています。インライン・ビューは高機能ですが、WITH句を最大限に使用するような形で問合せを構成すると、可読性およびメンテナンス性が大幅に向上します。

- この問合せでは、WITH句の使用を控えめにしています。一部のネストしたインライン・ビューは、WITH句の独立した副次句として記述することも可能です。たとえば、メインの問合せでは、1つの値を戻すのに2つのインライン・ビューを使用しています。これらのインライン・ビューは、シェア計算の分母を戻すために使用されています。ここでインライン・ビューを使用せずに、その処理をWITH句で行うようにすると、可読性が向上します。顧客の購買分析に関する例「顧客のバケットへのグループ化による顧客分析」を参照して、WITH句を最大限に使用する問合せと比較してください。

- SUM関数に対する引数の中で、CASE式を使用している点に注目してください。これらのCASE式をWHERE句の後に追加のデータ・フィルタとして機能させることで、SQLの記述が簡潔になっています。またこれらの式によって、指定の日付の売上列と、別の日付の別の列を合計することが可能になっています。

WITH prod_list AS                     --START: Top 20% of products
( SELECT prod_id prod_subset, cume_dist_prod
FROM                              --START: All products Sales for city subset
( SELECT s.prod_id, SUM(amount_sold),
CUME_DIST() OVER (ORDER BY SUM(amount_sold)) cume_dist_prod
FROM sales s, customers c, channels ch,  products p, times t
WHERE s.prod_id = p.prod_id AND p.prod_total_id = 1 AND
s.channel_id = ch.channel_id AND ch.channel_total_id = 1 AND
s.cust_id = c.cust_id AND
s.promo_id = 999 AND
s.time_id  = t.time_id AND t.calendar_quarter_id = 1776 AND
c.cust_city_id IN
(SELECT cust_city_id            --START: Top 20% of cities
FROM
(
SELECT cust_city_id, ((new_cust_sales - old_cust_sales)
/ old_cust_sales ) pct_change, old_cust_sales
FROM
(
SELECT cust_city_id, new_cust_sales, old_cust_sales
FROM
(          --START: Cities AND sales for 1 country in 2 periods
SELECT cust_city_id,
SUM(CASE WHEN t.calendar_quarter_id = 1776
THEN amount_sold  ELSE  0  END ) new_cust_sales,
SUM(CASE WHEN t.calendar_quarter_id = 1772
THEN amount_sold ELSE 0 END) old_cust_sales
FROM sales s, customers c, channels ch,
products p, times t
WHERE s.prod_id = p.prod_id AND p.prod_total_id = 1 AND
s.channel_id = ch.channel_id AND ch.channel_total_id = 1 AND
s.cust_id = c.cust_id AND c.country_id = 52790 AND
s.promo_id = 999 AND
s.time_id  = t.time_id AND
(t.calendar_quarter_id = 1776 OR t.calendar_quarter_id =1772)
GROUP BY cust_city_id
) cust_sales_wzeroes
WHERE old_cust_sales > 0
)  cust_sales_woutzeroes
)         --END: Cities and sales for country in 2 periods
WHERE old_cust_sales > 0 AND  pct_change >= 0.20)
--END: Top 20% of cities
GROUP BY s.prod_id
)  prod_sales                    --END: All products sales for city subset
WHERE cume_dist_prod > 0.8   --END: Top 20% products
)
--START: Main query bloc
SELECT  prod_id, ( (new_subset_sales/new_tot_sales)
-    (old_subset_sales/old_tot_sales)
) *100  share_changes
FROM
(                                --START: Total sales for country in later period
SELECT  prod_id,
SUM(CASE WHEN t.calendar_quarter_id = 1776
THEN amount_sold  ELSE  0  END )  new_subset_sales,
(SELECT SUM(amount_sold) FROM sales s, times t, channels ch,
customers c, countries co, products p
WHERE s.time_id  = t.time_id AND t.calendar_quarter_id = 1776 AND
s.channel_id = ch.channel_id AND ch.channel_total_id = 1 AND
s.cust_id = c.cust_id AND
c.country_id = co.country_id AND co.country_total_id = 52806 AND
s.prod_id = p.prod_id AND p.prod_total_id = 1 AND
s.promo_id = 999
)   new_tot_sales,

--END: Total sales for country in later period
--START: Total sales for country in earlier period
SUM(CASE WHEN t.calendar_quarter_id = 1772
THEN amount_sold  ELSE  0  END)  old_subset_sales,
(SELECT SUM(amount_sold) FROM sales s, times t, channels ch,
customers c, countries co, products p
WHERE s.time_id  = t.time_id AND t.calendar_quarter_id = 1772 AND
s.channel_id = ch.channel_id AND ch.channel_total_id = 1 AND
s.cust_id = c.cust_id AND
c.country_id = co.country_id AND co.country_total_id = 52806 AND
s.prod_id = p.prod_id AND p.prod_total_id = 1 AND
s.promo_id = 999
)   old_tot_sales
--END: Total sales for country in earlier period
FROM sales s, customers c, countries co, channels ch, times t
WHERE s.channel_id = ch.channel_id AND ch.channel_total_id = 1 AND
s.cust_id = c.cust_id AND
c.country_id = co.country_id AND co.country_total_id = 52806 AND
s.promo_id = 999 AND
s.time_id  = t.time_id AND
(t.calendar_quarter_id = 1776 OR t.calendar_quarter_id = 1772)
AND s.prod_id IN
(SELECT prod_subset FROM prod_list)
GROUP BY prod_id);

例2   欠損データを埋める売上予測

この問合せは、2000年と2001年の売上に基づいて2002年の売上を予測します。2000年と2001年の売上について最大の変化率を求めてから、これを2002年の売上に追加します。これは単純な計算ですが、1つ注意が必要な点があります。それは、多くの製品において、2000年と2001年に売上データのない月が存在することです。これらの空白値には、実際の売上が存在する月から算出した、その年の平均売上値を埋め込みます。またこの問合せでは、国別の通貨値をUSドルに変換します。最終的に、この問合せは2002年の予測値のみを戻します。

この例で使用しているテクニックは次のとおりです。

- MODEL句の前に、クロス結合を使用して考え得るすべてのデータ行を事前定義することで、MODEL句で必要となる処理の量を減らしています。

- 通貨の変換を行うために、MODEL句で参照モデルを使用しています。

- CV関数を多用することで、必要なルールの総数を3つにまで減らしています。

- 最も興味深い処理を行っているのは最後のルールです。ここでは、ネストしたルールを使用して通貨の変換係数を求めています。この式で必要となる国名を提供するため、参照モデルとメイン・モデルの両方で、国をそれぞれディメンションc、メジャーccとして定義しています。

この例の処理は、通貨の変換係数の参照表を作成するところから始まります。この表は、各国の各月の変換係数を格納します。表に挿入する行を指定するために、クロス結合を使用している点に注目してください。変換係数を設定する国は、例の目的に合せて1か国(カナダ)のみとしています。

CREATE TABLE currency (
country         VARCHAR2(20),
year            NUMBER,
month           NUMBER,
to_us           NUMBER);

INSERT INTO currency
(SELECT distinct
SUBSTR(country_name,1,20), calendar_year, calendar_month_number, 1
FROM countries
CROSS JOIN times t
WHERE calendar_year IN (2000,2001,2002)
);
UPDATE currency set to_us=.74 WHERE country='Canada';

次に示すのは、予測用の問合せです。まず、2つの副次句を含むWITH句を指定しています。最初の副次句では、2000年、2001年および2002年の製品ごとの月次売上高を国別に求めています。2つ目の副次句では、月レベルの個別時間値のリストを求めています。

WITH  prod_sales_mo AS       --Product sales per month for one country
(
SELECT country_name c, prod_id p, calendar_year  y,
calendar_month_number  m, SUM(amount_sold) s
FROM sales s, customers c, times t, countries cn, promotions p, channels ch
WHERE  s.promo_id = p.promo_id AND p.promo_total_id = 1 AND
s.channel_id = ch.channel_id AND ch.channel_total_id = 1 AND
s.cust_id=c.cust_id  AND
c.country_id=cn.country_id AND country_name='France' AND
s.time_id=t.time_id  AND t.calendar_year IN  (2000, 2001,2002)
GROUP BY cn.country_name, prod_id, calendar_year, calendar_month_number
),
-- Time data used for ensuring that model has all dates
time_summary AS(  SELECT DISTINCT calendar_year cal_y, calendar_month_number cal_m
FROM times
WHERE  calendar_year IN  (2000, 2001, 2002)
)
--START: main query block
SELECT c, p, y, m, s,  nr FROM (
SELECT c, p, y, m, s,  nr
FROM prod_sales_mo s
--Use partition outer join to make sure that each combination
--of country and product has rows for all month values
PARTITION BY (s.c, s.p)
RIGHT OUTER JOIN time_summary ts ON
(s.m = ts.cal_m
AND s.y = ts.cal_y
)
MODEL
REFERENCE curr_conversion ON
(SELECT country, year, month, to_us
FROM currency)
DIMENSION BY (country, year y,month m) MEASURES (to_us)
--START: main model
PARTITION BY (s.c c)
DIMENSION BY (s.p p, ts.cal_y y, ts.cal_m m)
MEASURES (s.s s, CAST(NULL AS NUMBER) nr,
s.c cc ) --country is used for currency conversion
RULES (
--first rule fills in missing data with average values
nr[ANY, ANY, ANY]
= CASE WHEN s[CV(), CV(), CV()] IS NOT NULL
THEN s[CV(), CV(), CV()]
ELSE ROUND(AVG(s)[CV(), CV(), m BETWEEN 1 AND 12],2)
END,
--second rule calculates projected values for 2002
nr[ANY, 2002, ANY] = ROUND(
((nr[CV(),2001,CV()] - nr[CV(),2000, CV()])
/ nr[CV(),2000, CV()]) * nr[CV(),2001, CV()]
+ nr[CV(),2001, CV()],2),
--third rule converts 2002 projections to US dollars
nr[ANY,y != 2002,ANY]
= ROUND(nr[CV(),CV(),CV()]
* curr_conversion.to_us[ cc[CV(),CV(),CV()], CV(y), CV(m)], 2)
)
ORDER BY c, p, y, m)
WHERE y = '2002'
ORDER BY c, p, y, m;

例3   顧客のバケットへのグループ化による顧客分析

顧客を理解する1つの方法として重要なのは、顧客の購買パターンを調べ、顧客ごとの利益率を明らかにすることです。これは、その顧客に営業活動を行う価値があるかどうか、どのような活動が有効かを判断するのに役立ちます。shサンプル・スキーマのデータセットには多数の顧客が含まれているため、利益率の分析は、高レベルのビューから始めるのが適しています。ここでは、顧客利益率のヒストグラムのためのデータを求め、利益率を10の範囲(ヒストグラム分析では通常「バケット」と呼ばれる)に分割します。国ごとに、月の集計レベルで次のデータを求めます。

- 10の等幅バケットからなる、顧客利益率のヒストグラムに必要なデータ。つまり、10の利益率バケットのそれぞれにグループ化される顧客の数を求めます。結果として取得される行は10行のみですが、大がかりな計算が必要になります。

利益率バケットごとに、次のデータも求めます。

- 顧客ごとの月間トランザクション数の中央値(同日の単一の顧客による単一のチャネルを介した購買を1つのトランザクションとして扱う)

- 顧客ごとの(各国通貨での)トランザクション・サイズの中央値

- 利益が最大の製品と最小の製品

- トランザクション数の中央値とトランザクション・サイズの中央値の、前年からの変化率

この例で使用しているテクニックは次のとおりです。

- WITH句を使用して問合せの可読性を向上させます。必要なデータを論理的なチャンクに分割し、各チャンクを専用のWITHの副次句で記述することで、ネストしたインライン・ビューと比較して可読性およびメンテナンス性が大幅に向上しています。WITH句を最大限に使用すると、メインのSELECT句で、取得したデータに対する計算を行わずに済みます。このことも、問合せの可読性およびメンテナンス性の向上に寄与しています。

- 等幅のヒストグラム・バケットを得るためのwidth_bucket、およびトランザクション・サイズとトランザクション数の中央値を求めるためのpercentile_contという2つの分析SQL関数を使用します。

この問合せには、データ・ウェアハウスの設計に起因する分析上の問題点が含まれています。それは、shのデータには各トランザクションのエントリおよびトランザクション数のエントリが含まれていないため、これらの数値については仮定を設定する必要があるということです。この問合せでは、最小主義に則って、同じ日に単一の顧客が単一のチャネルを介して購入した製品をすべて同じトランザクションに属するものとみなします。実際には、同じ日に同じチャネルで複数の購買を行っている顧客もいるはずなので、このアプローチでは、トランザクション数は必然的に実際よりも少なくなります。

次に示す問合せは、先頭部分cust_prod_mon_profitに対するマテリアライズド・ビューを作成してから実行する必要があることに注意してください。また、そのマテリアライズド・ビューを作成する前に、索引を2つ作成しておくことも必要です。このような準備作業をしないままこの問合せを実行すると、場合によっては終了までにかなりの時間がかかります。必要となる2つの索引および問合せの主要部分は次のとおりです。

CREATE BITMAP INDEX costs_chan_bix
ON costs (channel_id)
LOCAL NOLOGGING COMPUTE STATISTICS;

CREATE BITMAP INDEX costs_promo_bix
ON costs (promo_id)
LOCAL NOLOGGING COMPUTE STATISTICS;

WITH cust_prod_mon_profit AS
-- profit by cust, prod, day, channel, promo
(SELECT s.cust_id, s.prod_id, s.time_id,
s.channel_id, s.promo_id,
s.quantity_sold*(c.unit_price-c.unit_cost) profit,
s.amount_sold dol_sold, c.unit_price price, c.unit_cost cost
FROM sales s, costs c
WHERE s.prod_id=c.prod_id
AND s.time_id=c.time_id
AND s.promo_id=c.promo_id
AND s.channel_id=c.channel_id
AND s.cust_id in (SELECT cust_id FROM customers cst
WHERE cst.country_id = 52770
AND s.time_id IN (SELECT time_id FROM times t
WHERE t.calendar_month_desc = '2000-12'
),
-- Transaction Definition:  All products sold through a single channel to a
-- single cust on a single day are assumed to be sold in 1 transaction.
-- Some products in a transacton
-- may be on promotion
-- A customers  daily transaction amount is the sum of ALL products
-- purchased in the same channel in the same day
cust_daily_trans_amt  AS
(  SELECT cust_id, time_id, channel_id,  SUM(dol_sold) cust_daily_trans_amt
FROM  cust_prod_mon_profit
GROUP BY cust_id, time_id, channel_id
--A customers  monthly transaction count is the count of all channels
--used to purchase items in the same day, over all days in the month.
--It is really a count of the minimum possible number of transactions
cust_purchase_cnt  AS(  SELECT cust_id,  COUNT(*) cust_purchase_cnt
FROM  cust_daily_trans_amt
GROUP BY cust_id
),
--  Total profit for a customer over 1 month
cust_mon_profit AS
( SELECT cust_id, SUM(profit) cust_profit
FROM  cust_prod_mon_profit
GROUP BY cust_id
-- Minimum and maximum profit across all customer
-- sets endpoints for histogram data.
min_max_p AS
-- Note max profit + 0.1 to allow 10th bucket to include max value
(SELECT 0.1 + MAX(cust_profit) max_p, MIN(cust_profit) min_p
FROM cust_mon_profit),
-- Profitability bucket found for each customer
cust_bucket AS
(SELECT cust_id, cust_profit,
width_bucket(cust_profit,
min_max_p.min_p,
FROM cust_mon_profit,  min_max_p
-- Aggregated data needed for each bucket
histo_data AS
(  SELECT bucket,
bucket*(( max_p-min_p) /10) top_end , count(*)  histo_count
FROM  cust_bucket, min_max_p
GROUP BY bucket, bucket*(( max_p - min_p) /10)
-- Median count of transactions per cust per month median_trans_count AS
-- Find median count of transactions per cust per month
(SELECT cust_bucket.bucket,
PERCENTILE_CONT(0.5) WITHIN GROUP
(ORDER BY cust_purchase_cnt.cust_purchase_cnt) median_trans_count
FROM cust_bucket, cust_purchase_cnt
WHERE cust_bucket.cust_id=cust_purchase_cnt.cust_id
GROUP BY cust_bucket.bucket
-- Find Mmedian transaction size for custs by profit bucket
cust_median_trans_size AS
(  SELECT cust_bucket.bucket,
PERCENTILE_CONT(0.5) WITHIN GROUP
(ORDER BY cust_daily_trans_amt.cust_daily_trans_amt)
cust_median_trans_ size
FROM cust_bucket, cust_daily_trans_amt
WHERE cust_bucket.cust_id=cust_daily_trans_amt.cust_id
GROUP BY cust_bucket.bucket
-- Profitability of each product sold within each bucket
bucket_prod_profits AS
(  SELECT  cust_bucket.bucket, prod_id, SUM(profit) tot_prod_profit
FROM  cust_bucket, cust_prod_mon_profit
WHERE  cust_bucket.cust_id=cust_prod_mon_profit.cust_id
GROUP BY cust_bucket.bucket, prod_id
),  -- Most and least profitable product by bucket
prod_profit AS
(  SELECT bucket, MIN(tot_prod_profit) min_profit_prod,
MAX(tot_prod_profit) max_profit_prod
FROM bucket_prod_profits
GROUP BY bucket
-- Main query block
SELECT  histo_data.bucket, histo_data.histo_count,
median_trans_count.median_trans_count,
cust_median_trans_size.cust_median_trans_size,
prod_profit.min_profit_prod, prod_profit.max_profit_prod
FROM  histo_data, median_trans_count, cust_median_trans_size,
prod_profit
WHERE  histo_data.bucket=median_trans_count.bucket
AND  histo_data.bucket=cust_median_trans_size.bucket
AND  histo_data.bucket=prod_profit.bucket;

例4   高頻度項目セット

あるマーケティング・マネージャが、自社製品の各種ホワイト・ペーパーについて、ユーザーがそれぞれのセッションでどのような種類のものをダウンロードするかを把握しようとしているとします。その場合このマネージャが必要としている情報は、ホワイト・ペーパーのどのようなグルーピングが頻度の高い項目であるかということです。そのような情報は、ダウンロードされたホワイト・ペーパーごとにユーザーIDとセッションIDがWebサイトのアクティビティ・ログに記録されていれば、すでに組み込まれている高頻度項目機能を使用することで容易に取得できます。まず、個々のホワイト・ペーパーについてダウンロード数を集計したリストを次に示します。(この例では、Oracle製品のホワイト・ペーパー名を使用しています。)

White paper titles                                               #
-------------------------------------------------------          ----
Table Compression in Oracle Database 10g                          696
Field Experiences with Large Data Warehouses                      439
Key Data Warehouse Features: A Comparative Performance Analysis   181
Materialized Views in Oracle Database 10g                         167
Parallel Execution in Oracle Database 10g                         166

次に示すのは、このような分析に使用できる問合せの一例です。この問合せには、テーブル・ファンクションとしてDBMS_FREQUENT_ITEMSET.FI_TRANSACTIONALが使用されています。問合せの構造の詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。この問合せでは、同一のセッションの中でダウンロードされたホワイト・ペーパーの組合せの項目セットが戻されます。

SELECT itemset, support, length, rnk
FROM
(SELECT itemset, support, length,
RANK(), OVER (PARTITION BY length ORDER BY support DESC) rnk
FROM
(SELECT CAST(itemset AS fi_char) itemset, support, length, total_tranx
FROM table(DBMS_FREQUENT_ITEMSET.FI_TRANSACTIONAL
(CURSOR(SELECT session_id, command
FROM web_log
WHERE time_stamp BETWEEN '01-APR-2002' AND '01-JUN-2002'),
(60/2600), 2, 2, CURSOR(SELECT 'a' FROM DUAL WHERE 1=0),
CURSOR(SELECT 'a' FROM DUAL WHERE 1=0)))))
WHERE rnk <= 10;

上位3項目が次のような結果であったとします。

White paper titles                                                    #
---------------------------------------------------------         -----
Table Compression in Oracle Database 10g                            115
Field Experiences with Large Data Warehouses

Data Warehouse Performance Enhancements with Oracle Database 10g    109
Oracle Performance and Scalability in DSS Environments

Materialized Views in Oracle Database 10g                           107
Query Optimization in Oracle Database 10g

この分析からは、興味深い結果が導き出されています。個々のホワイト・ペーパーのダウンロード数のリストを見ると、「Table Compression in Oracle10g」がすべてのホワイト・ペーパーの中で最上位にあるので、ダウンロード数の多かったホワイト・ペーパーの組合せでも、その大部分にこのホワイト・ペーパーが含まれていると予想されます。しかし、上位3つの組合せのうち、このホワイト・ペーパーが含まれる組合せは1つしかありません。

高頻度項目セットを使用してWebのログ情報を分析すれば、ダウンロード数の多いホワイト・ペーパーを表示するだけの簡単なレポートよりもはるかに多くの情報を抽出することが可能になります。この結果から、このWebサイトの訪問者はセッションごとに特定のテーマに沿った情報を探す傾向が強いということがわかります。スケーラビリティに関心のある訪問者は、当然、圧縮についてのホワイト・ペーパーと大規模データ・ウェアハウスについてのホワイト・ペーパーをダウンロードし、複合問合せ機能に関心のある訪問者は、問合せの最適化についてのホワイト・ペーパーとマテリアライズド・ビューについてのホワイト・ペーパーをダウンロードすると考えられます。このような情報は、マーケティング・マネージャにとっては、将来的にどのような内容のホワイト・ペーパーを執筆すればよいかを判断するのに役立ち、Webデザイナにとっては、Webサイトの構成についてのヒントとして役立ちます。

詳細は、「高頻度項目セット」を参照してください。

用語集

加算的

加算することでサマリーできるファクト(またはメジャー)を示します。加算ファクトは、最も一般的なタイプのファクトです。たとえば、販売価格、原価、収益などがこれに該当します。「非加算的」および「準加算的」と対比してください。

アドバイザ

「SQLアクセス・アドバイザ」を参照してください。

集計

サマリーされたデータ。たとえば、特定製品の売上数量を1日、1か月、四半期および1年ごとに集計できます。

集計操作

複数のデータ値を1つの値に集約する処理。たとえば、1日単位で集めた販売データを週レベルに集計したり、週のデータを月レベルに集計するなどの処理がこれに該当します。その後、データは集計データとして参照できます。集計という用語はサマリーと同義であり、集計データはサマリー・データと同義です。

祖先

階層内で、特定の値よりも上位のレベルにある値。たとえば、Timeディメンションでは、値1999は値Q1-99とJan-99の祖先です。

属性

1つ以上のレベルの特徴を説明した特性。たとえば、衣料品製造業の製品ディメンションには品目と呼ばれるレベルが含まれ、その中に色という属性があります。属性は、エンド・ユーザーが類似の特性に基づいてデータを選択できる論理グループを表します。

リレーショナル・モデルにおける属性は、エンティティの特性として定義されます。Oracle Database 10gの場合、属性は単一レベルの各要素を特徴付けるディメンションの列です。

カーディナリティ

OLTPの観点では、表内の行数を指します。データ・ウェアハウスの観点では、一般に、列内の個別値の数を指します。データ・ウェアハウスのほとんどのDBAにとっては、カーディナリティ度のほうがより重要な問題点です。

チェンジ・セット

トランザクション一貫性が保証される、論理的にグループ化された変更データのセット。これには、1つ以上のチェンジ・テーブルが含まれます。

チェンジ・テーブル

単一のソース・テーブルの変更データを含むリレーショナル表。チェンジ・データ・キャプチャのサブスクライバに対して、チェンジ・テーブルはパブリケーションとして知られます。

子

階層内で、特定の値の直下のレベルにある値のことです。たとえば、Timeディメンションでは、値Jan-99は値Q1-99の子です。子値が複数の階層に属している場合は、1つの値が複数の親の子になることもあります。

クレンジング

ソース・データの非一貫性を解決し、異常を修正する処理。通常は、ETL処理の一部です。

コモン・ウェアハウス・メタデータ(CWM)

Oracleデータ・ウェアハウスおよび意思決定支援で使用される標準リポジトリ。CWMリポジトリ・スキーマは他の製品が共有できるスタンドアロン製品で、それぞれ、その製品が作成するCWMリポジトリ内のオブジェクトのみを所有します。

クロス積

複数セットの要素群を組み合せる方法。たとえば、2つの列がある場合、最初の列の各要素は2番目の列の各要素と組み合せられます。単純例を次に示します。

Col1   Col2   Cross Product
----   ----   -------------
a      c      ac
b      d      ad
bc
bd

クロス積は、グルーピング・セットの連結時に行われます。第21章「データ・ウェアハウスにおける集計のためのSQL」を参照してください。

データ・マート

販売、マーケティング、金融など、特定のビジネス分野に対して設計されたデータ・ウェアハウス。依存型のデータ・マートの場合、データは企業全体のデータ・ウェアハウスから導出されます。非依存型のデータ・マートの場合、データはソースから直接収集されます。

データソース

ウェアハウスにデータを提供するデータベース、アプリケーション、リポジトリまたはファイル。

データ・ウェアハウス

トランザクション処理用ではなく、問合せおよび分析用に設計されたリレーショナル・データベース。データ・ウェアハウスには、通常、トランザクション・データから導出された履歴データが含まれますが、別のソースからのデータを含めることもできます。データ・ウェアハウスにより、分析ワークロードとトランザクション・ワークロードを分離できます。また企業は、複数のソースのデータを統合できるようになります。

データ・ウェアハウス環境は、リレーショナル・データベースに加え、ETLソリューション、分析SQLエンジン、クライアント分析ツール、およびデータ収集とビジネス・ユーザーへのデータ配信の処理を管理するその他のアプリケーションで構成されることが多いです。

カーディナリティ度

表内の列の個別値の数を表内の行の合計数で割ったものです。これは、作成する索引を決定する際に特に重要です。通常、カーディナリティ度の低い列にはビットマップ索引、カーディナリティ度の高い列にはBツリー索引を使用します。原則として、カーディナリティ度が1%未満の場合にビットマップ索引を使用します。

非正規化

表内の冗長性を許す処理。「正規化」と対比してください。

導出ファクト(またはメジャー)

算術演算またはデータ変換を使用して既存のデータから生成されたファクト(またはメジャー)。例としては、平均、合計、割合、差などがあります。

ディテール

「ファクト表」を参照してください。

ディテール表

「ファクト表」を参照してください。

ディメンション

一般に、2通りの方法で使用されます。

- データセットのメンバーを指定するために使用される特性を示す一般的な用語。売上指向のデータ・ウェアハウスにおける最も一般的なディメンションは、時間、地理および製品の3つです。ほとんどのディメンションが階層を持ちます。

- 問合せがディメンションをナビゲートできるようにデータベース内に定義されたオブジェクト。Oracle Database 10gの場合、ディメンションは、1組の列セット間の階層(親/子)関係を定義するデータベース・オブジェクトです。Oracle Expressの場合、ディメンションは値リストで構成されるデータベース・オブジェクトです。

ディメンション表

時間、部門、所在地、製品などの階層情報および分類情報として表される企業のビジネス・エンティティを記述します。参照表と呼ばれる場合もあります。

ディメンション値

ディメンションを構成するリストの一要素。たとえば、コンピュータ会社では、製品ディメンションにLAPPCやDESKPCなどのディメンション値を持ちます。地理ディメンション内の値には、BostonおよびParisなどが含まれる場合があります。また、時間ディメンション内の値には、MAY96やJAN97などが含まれる場合があります。

ドリル

1つの項目から一連の関連項目にナビゲートすることです。ドリル操作は通常、ある階層内のレベル内(またはレベル間)での上下へのナビゲートを伴います。データを選択する際、階層内でドリルダウンすれば階層が開き、ドリルアップすれば階層が閉じます。

ドリルダウン

ビューを拡張して、親の値に関連付けられている子の値を階層内に含めることです。

ドリルアップ

階層内で親の値に関連付けられている子の値のリストを閉じることです。

要素

オブジェクトまたはプロセス。たとえば、ディメンションはオブジェクト、マッピングはプロセスであり、両方とも要素です。

エンティティ

データベースのモデル化に使用されます。リレーショナル・データベースでは通常、表にマップされます。

ETL

Extraction(抽出)、Transformation(変換)およびLoading(ロード)の略。ソース・データにアクセスして操作を行い、データ・ウェアハウスへロードする方法を意味します。これらの処理の実行順序は様々です。

ETLのかわりに、ETT(extraction, transformation, transportation)やETM(extraction, transformation, move)が使用される場合もあります。

抽出

ETLの初期フェーズにおいてソースからデータを取り出す処理。

ファクト

調査や分析の対象となるデータで、通常は数値データや加算的データ。たとえば、販売価格、原価、収益などがこれに該当します。ファクトとメジャーは同じ意味で、ファクトは主にリレーショナル環境で使用され、メジャーは主に多次元環境で使用されます。導出ファクト(またはメジャー)は、算術演算やデータ変換を使用して既存のデータから生成されます。

ファクト表

ファクトを格納する、スター・スキーマ内の表。多くの場合、ファクト表には、ファクトを格納する列と、ディメンション表の外部キーとなる列の2種類の列があります。通常、ファクト表の主キーは、その表のすべての外部キーで構成されるコンポジット・キーです。

ファクト表には、詳細レベルのファクトまたは集計されたファクト(集計されたファクトを含むファクト表は、サマリー表と呼ばれることが多い)のいずれかが含まれています。通常、ファクト表には同じ集計操作レベルのファクトが含まれています。

高速リフレッシュ

マテリアライズド・ビューに対して変更されたデータのみを適用する操作。この操作によって、マテリアライズド・ビューを一から再作成する必要がなくなります。

ファイルから表へのマッピング

フラット・ファイルからウェアハウス内の表へのデータのマップ。

階層

データを編成する手段として順序付けされたレベルを使用する論理構造。データ集計を定義するために使用できます。たとえば、時間ディメンションでは、階層を使用して月レベルから四半期レベル、年レベルへとデータを集計できます。階層は、Oracleでディメンション・オブジェクトの一部として定義できます。また、ドリル操作のナビゲーション・パスの定義にも使用できますが、この場合、階層内のレベルは必ずしも集計された合計を示している必要はありません。

高位限界

サブスクリプション・ウィンドウの最も新しい行。

レベル

階層内での位置。たとえば、時間ディメンションは、月レベル、四半期レベルおよび年レベルのデータを表す階層を持ちます。

レベル値の表

ディメンションおよび階層の一部として作成したレベルの値またはデータを格納するデータベース表。

低位限界

サブスクリプション・ウィンドウの最も古い行。

マッピング

ソース・オブジェクトとターゲット・オブジェクトとの間の関係およびデータ・フローに関する定義。

マテリアライズド・ビュー

ファクト表(場合によってはディメンション表)の集計データまたは結合データで構成される事前計算表。サマリーまたは集計表とも呼ばれます。

メジャー

「ファクト」を参照してください。

メタデータ

データおよびその他の構造(オブジェクト、ビジネス・ルール、ビジネス・プロセスなど)を記述するデータ。たとえば、データ・ウェアハウスのスキーマ設計は、通常、メタデータとしてリポジトリに格納され、データ・ウェアハウスの作成と移入に使用するスクリプトを生成するために使用されます。メタデータはリポジトリに含まれます。

データの例: ソースからターゲットへの変換に関する定義、データ・ウェアハウスの作成と移入に使用されます。情報の例: 表、列、関連項目の定義、関連するモデル・ツール内に格納されます。ビジネス・ルールの例: 1,000個を販売した後10パーセントの値引を行います。

モデル

作成する内容を示すオブジェクト。典型的なスタイル、計画、設計。また、データ・ウェアハウスの構造を定義するメタデータにもなります。

非加算

加算することでサマリーできないファクト(またはメジャー)を示します。非加算の例には、平均があります。「加算的」および「準加算的」と対比してください。

正規化

リレーショナル・データベースにおいて、データを複数の表に分離することによりデータの冗長性を取り除くプロセス。「非正規化」と対比してください。

データを複数の表に分割し、データの冗長性を排除する処理。

OLTP

「オンライン・トランザクション処理(OLTP)」を参照してください。

オンライン・トランザクション処理(OLTP)

オンライン・トランザクション処理。OLTPシステムは、高速で信頼性の高いトランザクション処理用に最適化されています。データ・ウェアハウス・システムに比べると、ほとんどのOLTPシステムには、比較的少数の行と多数の表のグループが含まれます。

パラレル化

いくつかのプロセスが作業の一部を処理できるようにタスクを分解することです。複数のCPUがそれぞれの部分を同時に実行すると、パフォーマンスを大きく向上させることができます。

パラレル実行

いくつかのプロセスが作業の一部を処理できるようにタスクを分解することです。複数のCPUがそれぞれの部分を同時に実行すると、パフォーマンスを大きく向上させることができます。

親

階層内の所定の値より上のレベルにある値。たとえば、時間ディメンションでは、値Q1-99(99年第1四半期)は、子値Jan-99(99年1月)の親とされる場合があります。

パーティション

非常に大きな表および索引は、操作が難しく時間がかかる可能性があります。管理性を改善するために、表と索引をパーティションと呼ばれる小さい部分に分解できます。

ピボット

入力ストリーム内の各レコードが、データ・ウェアハウスの適切な表にある多数のレコードに変換される変換処理。これは、リレーショナルでないデータベースからデータを取り出す際に特に重要です。

パブリケーション

単一のソース・テーブルの変更データを含むリレーショナル表。チェンジ・データ・キャプチャのパブリッシャは、チェンジ・テーブルとしてパブリケーションを参照します。

パブリケーションID

チェンジ・データ・キャプチャが、パブリッシャによって定義された各チェンジ・テーブルに割り当てる固有の数値。

パブリッシャ

通常、チェンジ・データ・キャプチャ・システムを構成するスキーマ・オブジェクトの作成とメンテナンスを担当するデータベース管理者。

クエリー・リライト

マテリアライズド・ビュー(事前に計算されたもの)を使用して問合せに迅速に答えるメカニズム。

リフレッシュ

マテリアライズド・ビューを変更して新しいデータを反映するメカニズム。

リライト

「クエリー・リライト」を参照してください。

スキーマ

関連するデータベース・オブジェクトの集まり。リレーショナル・スキーマは、データベース・ユーザーIDでグルーピングされ、表やビューなどのオブジェクトを含みます。このマニュアルでは、shというサンプル・スキーマを使用しています。特殊なタイプのスキーマとして、スノーフレーク・スキーマおよびスター・スキーマの2つがあります。

準加算的

全ディメンションについてではなく、一部のディメンションによって加算することでサマリーできるファクト(またはメジャー)を示します。準加算の例には、人数や手持在庫があります。「加算的」および「非加算的」と対比してください。

スライスおよびダイス

データの取得および操作を指す非公式用語。データ・ウェアハウスは、それぞれの軸がディメンションを表したデータのキューブ(立方体)と見ることができます。データをスライスするとは、ディメンションの一部または全部のメジャーと値を指定してキューブのピース(スライス)を取得することです。データ・スライスの取得時に、スライスを細切れ(ダイス)したように多数の小さなピースにし、データ列と行を移動したり並べ替えることもできます。適切にスライスおよびダイスされたシステムでは、大量のデータのナビゲーションが容易になります。

スノーフレーク・スキーマ

各ディメンション表の一部または全部が正規化されたタイプのスター・スキーマ。

ソース

データ・ウェアハウス内のデータが導出されるデータベース、アプリケーション、ファイルまたはその他のデータ保管場所。

ソース・システム

データ・ウェアハウス内のデータが導出されるデータベース、アプリケーション、ファイルまたはその他のデータ保管場所。

ソース・テーブル

ソース・データベース内の表。

SQLアクセス・アドバイザ

ユーザーが目標とするパフォーマンスを実現できるように、特定のワークロードに適切なマテリアライズド・ビューのセット、マテリアライズド・ビュー・ログ、パーティション、索引を推奨します。Oracle Enterprise ManagerのGUIであり、DBMS_ADVISORパッケージと同様の機能を持ちます。

ステージング・エリア

ウェアハウスに入る前にデータが処理される場所。

ステージング・ファイル

ウェアハウスに入る前のデータ処理に使用されるファイル。

スター・クエリー

ファクト表および多数のディメンション表を結合するものです。各ディメンション表は、主キーから外部キーへの結合を使用してファクト表に結合されます。ただし、ディメンション表同士は結合されません。

スター・スキーマ

多次元データ・モデルを表現するように設計されたリレーショナル・スキーマ。1つ以上のファクト表と、外部キーを介して関連付けられている1つ以上のディメンション表で構成されます。

サブジェクト領域

組織の役割、知識領域を表現したり、識別するための分類方法。通常、1つのデータ・マートは、販売、マーケティングまたは地域などの1つのサブジェクト領域をサポートするために開発されます。

サブスクライバ

パブリッシュされた変更データのコンシューマ。通常はアプリケーションです。

サブスクリプション

単一チェンジ・セット内の1つ以上の対象ソース・テーブルの変更データに対するアクセスを制御するチェンジ・データ・キャプチャ・サブスクライバのメカニズム。サブスクリプションには1つ以上のサブスクライバ・ビューが含まれます。

サブスクリプション・ウィンドウ

サブスクライバが現在サブスクライバ・ビューで参照できるチェンジ・データ・キャプチャ・パブリケーションの行範囲を定義するメカニズム。

サマリー

「マテリアライズド・ビュー」を参照してください。

サマリー・アドバイザ

現在は、SQLアクセス・アドバイザに置き換えられています。

ターゲット

ETL処理過程において中間的または最終的な結果を保持します。ETL処理全体のターゲットは、データ・ウェアハウスです。

第3正規形

正規化を通してデータの冗長性を最小化する、古典的なリレーショナル・データベース・モデリング技法。

第3正規形スキーマ

OLTPシステムで一般的に使用されているものと同じ種類の正規化を使用するスキーマ。大規模なデータ・ウェアハウス、特に、データのロード要求が多く、データ・マートへのデータの入力および長時間実行問合せの実行に使用される環境用として選択されることがあります。「スノーフレーク・スキーマ」および「スター・スキーマ」と比較してください。

変換

データを操作する処理。コピー操作以外の操作は変換です。変換の例には、複数のソース・テーブルからのデータのクレンジング、集計および統合があります。

転送

コピーまたは変換したデータをソースからデータ・ウェアハウスに移動する処理。「変換」と比較してください。

一意識別子

同じ項目が複数の場所に表示される場合に、その項目を区別することを目的とする識別子。

更新ウィンドウ

ウェアハウスの更新に使用できる時間の長さ。たとえば、夜間の8時間がウェアハウスの更新に当てられます。

更新頻度

新しいデータでデータ・ウェアハウスが更新される頻度。たとえば、ウェアハウスはOLTPシステムから毎晩更新できます。

妥当性チェック

メタデータ定義および構成パラメータを検証する処理。

バージョニング

新規要件および変更に対応して、データ・ウェアハウス・プロジェクトの新規バージョンを作成する機能。

索引
A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X

A

集計, 9.2.1, 19.7.5

計算性チェック, 19.1.3.4

ALL_PUBLISHED_COLUMNSビュー, 17.9.6 ALTER MATERIALIZED VIEW文, 9.3 ディメンションの変更, 11.6 割賦償還額

計算, 23.5

分析ツール

説明, 1.3.1.9

分析関数, 23.3.5

概念, 22.1

分析処理

マテリアライズド・ビュー, 10.2

アプリケーション

データ・ウェアハウス

スター・クエリー, 20.3

意思決定支援システム(DSS), 7.1.1 アーキテクチャ

データ・ウェアハウス, 1.2

非同期AutoLogの公開

ソース・データベースのパフォーマンスへの影響, 17.6.2, 17.6.2, 17.6.2

非同期AutoLogの公開

待機時間, 17.6.2, 17.6.2

ステージング・データベースの場所, 17.6.2, 17.6.2

要件, 17.6.1.1

データベース初期化パラメータの設定, 17.6.3.4

非同期チェンジ・データ・キャプチャ

アーカイブREDOログ・ファイル, 17.11.1

ARCHIVELOGMODE, 17.11.1

サプリメンタル・ロギング, 17.11.2

サプリメンタル・ロギング, 17.2.2

非同期チェンジ・データ・キャプチャ

サポートされるOracleの組込みデータ型の列, 17.11.4

非同期チェンジ・セット

無効化, 17.9.2.2

有効化, 17.9.2.2

エクスポート, 17.9.5.1

インポート, 17.9.5.1

管理, 17.9.2

キャプチャ・エラーからのリカバリ, 17.9.2.4

例, 17.9.2.4.1, 17.9.2.4.2

DDLの削除, 17.9.2.4.2 終了値の指定, 17.9.2.1 開始値の指定, 17.9.2.1 DDLでのキャプチャの停止, 17.9.2.3

例外の文, 17.9.2.3

非同期チェンジ・テーブル

エクスポート, 17.9.5.1

インポート, 17.9.5.1

非同期HotLogの公開

ソース・データベースのパフォーマンスへの影響, 17.6.2

非同期HotLogの公開

待機時間, 17.6.2

ステージング・データベースの場所, 17.6.2

要件, 17.6.1.1

データベース初期化パラメータの設定, 17.6.3.2, 17.6.3.3, 17.6.3.4, 17.6.3.4

属性, 2.2, 11.2 自動ストレージ管理, 4.2

B

バインド変数

クエリー・リライト, 19.3.11

ビットマップ索引, 7.1

NULL, 7.1.3

パーティション表, 7.1.4

パラレル問合せおよびDML, 7.1.1, 7.1.1

ビットマップ結合索引, 7.1.5 Bツリー索引, 7.2

ビットマップ索引との比較, 7.1.2

作成方法, 9.3.4 ビジネス・インテリジェンス

問合せ, 24

ビジネス・ルール

違反, 15.4.1

C

キャプチャ・エラー

リカバリ, 17.9.2.4

カーディナリティ

カーディナリティ度, 7.1.2

CASE式, 22.7.3 セル参照, 23.2.4 チェンジ・データ・キャプチャ, 13.2.3

非同期

Streams適用プロセス, 17.6.4

Streams取得プロセス, 17.6.4

サブスクライバの利点, 17.1.3.2 モードの選択, 17.6.2 DDLでの停止の影響, 17.9.2.3 待機時間, 17.6.2 ステージング・データベースの場所, 17.6.2 データ・キャプチャのモード

非同期AutoLog, 17.2.2.3

非同期分散HotLog, 17.2.2.2

非同期HotLog, 17.2.2.1

同期, 17.2.1

Oracle Data Pump, 17.9.5 データベースからの削除, 17.12 ダイレクト・パス・インサート文の制限, 17.10.1 設定, 17.6 ソース・データベースのパフォーマンスへの影響, 17.6.2 静的データ・ディクショナリ・ビュー, 17.5 サポートされるエクスポート・ユーティリティ, 17.9.5 サポートされるインポート・ユーティリティ, 17.9.5 同期, 13.2.3.3 インストールされるシステム・トリガー, 17.12 チェンジ・データ・キャプチャのパブリッシャ

デフォルト表領域, 17.6.1.2

チェンジ・セット

定義, 17.3

無効化の影響, 17.9.2.2

非同期の管理, 17.9.2

同期チェンジ・データ・キャプチャ, 17.3

チェンジ・ソースとの有効な組合せ, 17.3.1

チェンジ・ソース

非同期AutoLogチェンジ・データ・キャプチャ, 17.2.2.3

非同期分散HotLogチェンジ・データ・キャプチャ, 17.2.2.2

表されるデータベース・インスタンス, 17.3.1

定義, 17.1.3.1, 17.1.3.1

HOTLOG_SOURCE, 17.2.2.1

SYNC_SOURCE, 17.2.1

チェンジ・セットとの有効な組合せ, 17.3.1

チェンジ・テーブル

列の追加, 17.9.6

制御列, 17.9.4.2

定義, 17.1.3.1

削除, 17.9.4.7

アクティブなサブスクライバで削除, 17.9.4.7

SQL文のDROP USER CASCADEの影響, 17.9.4.7

エクスポート, 17.9.5

サブスクライバへのアクセス権の付与, 17.9.4.5

インポート, 17.9.5

チェンジ・データ・キャプチャ用のインポート, 17.9.5.3

管理, 17.9.4

すべてパージ、指定のチェンジ・セット, 17.9.4.6

すべてパージ、ステージング・データベース上, 17.9.4.6

指定してパージ, 17.9.4.6

不要なデータのパージ, 17.9.4.6

参照されるソース・テーブル, 17.9.4.1

作成される表領域, 17.9.4.1

変更値選択, 17.1.1 列

カーディナリティ, 7.1.2

COMMIT_TIMESTAMP$

制御列, 17.9.4.2

共通結合, 19.1.3.1.1 COMPLETE句, 9.3.7 完全リフレッシュ, 16.3.1 複合問合せ

スノーフレーク・スキーマ, 20.3.1

コンポジット

列, 21.6

圧縮

「データ・セグメントの圧縮」を参照, 9.3.3

連結グルーピング, 21.7 連結ROLLUP, 21.10.1 構成

帯域幅, 4.1.1

制約, 8.1, 11.4

外部キー, 8.3.2

RELY, 8.3.3

状態, 8.2

一意, 8.3.1

ビュー, 8.3.7, 19.3.6

パーティション化, 8.3.6

クエリー・リライト, 19.7.1

制御列

行内の変更列を示すために使用, 17.9.4.3

制御列

COMMIT_TIMESTAMP$, 17.9.4.2

CSCN$, 17.9.4.2

OPERATION$, 17.9.4.2

ROW_ID$, 17.9.4.2

RSID$, 17.9.4.2

SOURCE_COLMAP$, 17.9.4.2

解釈, 17.9.4.3

SYS_NC_OID$, 17.9.4.2 TARGET_COLMAP$, 17.9.4.2

解釈, 17.9.4.3

TIMESTAMP$, 17.9.4.2 USERNAME$, 17.9.4.2 XIDSEQ$, 17.9.4.2 XIDSLT$, 17.9.4.2 XIDUSN$, 17.9.4.2 コストベースのリライト, 19.1.1 CREATE DIMENSION文, 11.2 CREATE MATERIALIZED VIEW文, 9.3

クエリー・リライトの有効化, 18.2

CREATE SESSION権限, 17.6.1.1 CREATE TABLE権限, 17.6.1.1 CSCN$

制御列, 17.9.4.2

CUBE句, 21.3

部分的, 21.3.3

使用するとき, 21.3.1

キューブ

階層的, 10.2.1

マテリアライズド・ビュー, 10.2.1

CUME_DIST関数, 22.2.1.3

D

データ

パーティション化, 5.1

パージ, 16.2.3, 16.2.3

充足性チェック, 19.1.3.2

変換, 15.3

転送, 14.2.1

データの圧縮

「データ・セグメントの圧縮」を参照, 9.3.3

データ・キューブ

階層的, 21.7.1

データの稠密化, 22.5

時系列の計算, 22.6

スパースなデータ, 22.5.2

データ・ディクショナリ

非同期チェンジ・データ・キャプチャ, 17.7.4

データ抽出

チェンジ・データ・キャプチャを使用する場合と使用しない場合, 17.1.2

データ・マート, 1.2.3 データ・ルール

違反, 15.4.2

データ・セグメントの圧縮, 3.2.3.1

マテリアライズド・ビュー, 9.3.3

パーティション化, 3.2.3.1

データ変換

マルチステージ, 15.1.1.1

パイプライン, 15.1.1.2

データ・ウェアハウス, 9.1

アーキテクチャ, 1.2

ディメンション表, 9.1.6

ディメンション, 20.3

ファクト表, 9.1.6

論理設計, 2.2

物理設計, 3.1

リフレッシュのヒント, 16.3.9

スター・クエリー, 20.3

データベース

ステージング, 9.1

データベース初期化パラメータ

Streams値の変更時の調整, 17.6.4

デート・フォールディング

クエリー・リライト, 19.3.5

DBAロール, 17.6.1.1 DBMS_ADVISOR.TUNE_MVIEWパッケージ, 9.3.5 DBMS_CDC_PUBLISHパッケージ, 17.1.3.1

使用に必要な権限, 17.6.1.1

DBMS_CDC_PUBLISH.DROP_CHANGE_TABLE PL/SQLプロシージャ, 17.9.4.7 DBMS_CDC_PUBLISH.PURGE PL/SQLプロシージャ, 17.9.4.6, 17.9.4.6 DBMS_CDC_PUBLISH.PURGE_CHANG_SET PL/SQLプロシージャ, 17.9.4.6 DBMS_CDC_PUBLISH.PURGE_CHANGE_TABLE PL/SQLプロシージャ, 17.9.4.6 DBMS_CDC_SUBSCRIBEパッケージ, 17.1.3.2 DBMS_CDC_SUBSCRIBE.PURGE_WINDOW PL/SQLプロシージャ, 17.9.4.6 DBMS_ERRORパッケージ, 15.4.2.2 DBMS_JOBパッケージ, 17.9.4.6 DBMS_MVIEWパッケージ, 16.3.5, 16.3.5

EXPLAIN_MVIEWプロシージャ, 9.7.1

EXPLAIN_REWRITEプロシージャ, 19.6.2

REFRESHプロシージャ, 16.3, 16.3.6

REFRESH_ALL_MVIEWSプロシージャ, 16.3

REFRESH_DEPENDENTプロシージャ, 16.3

DBMS_STATSパッケージ, 19.1.1 意思決定支援システム(DSS)

ビットマップ索引, 7.1.1

カーディナリティ度, 7.1.2 DENSE_RANK関数, 22.2.1.1 稠密化

データ, 22.5

設計

論理, 3.1

物理, 3.1

ディメンション・レベル

スキップ, 11.2

ディメンション表, 2.4, 9.1.6

正規化, 11.2.3

ディメンショナル・モデリング, 2.2 ディメンション, 2.4.2, 11.1, 11.4

変更, 11.6

分析, 21.1.1

作成, 11.2

定義, 11.1

ディメンション表, 9.1.6

削除, 11.7

階層, 2.4.2.1

階層の概要, 2.4.2.1

複数, 21.1.1

レベルのスキップ, 11.2

スター型結合, 20.3

スター・クエリー, 20.3

妥当性チェック, 11.5

クエリー・リライト, 19.7.2

ダイレクト・パス・インサート文

チェンジ・データ・キャプチャの制限, 17.10.1

ディスクの冗長性, 4.1.3 DMLアクセス権

サブスクライバ, 17.9.4.5

ダウンストリーム・キャプチャ, 17.7.4 ドリルダウン, 11.1

階層, 11.1

DROP MATERIALIZED VIEW文, 9.3

事前作成表, 9.4

削除

ディメンション, 11.7

マテリアライズド・ビュー, 9.6

チェンジ・テーブルの削除, 17.9.4.7

E

ENFORCEDモード, 18.2.3

エンティティ, 2.2

エラー・ロギング, 15.4

表, 15.4.2.2

エラー

処理, 15.4

ORA-31424, 17.9.4.7

ORA-31496, 17.9.4.7

ETL, 「抽出、変換、ロード(ETL)」を参照, 12.1 EXCHANGE PARTITION文, 8.3.6 EXECUTE権限, 17.6.1.1 EXECUTE_CATALOG_ROLE権限, 17.6.1.1 実行計画

スター型変換, 20.4.2.2

EXPAND_GSET_TO_UNIONヒント, 19.3.8.2, 19.7.10.4 EXPLAIN PLAN文, 19.6.1

スター型変換, 20.4.2.2

EXPLAIN_REWRITEプロシージャ, 19.6.2 エクスポート

チェンジ・テーブル, 17.9.5

非同期チェンジ・セット, 17.9.5.1

非同期チェンジ・テーブル, 17.9.5.1

EXPユーティリティ, 13.3.1.3

式の一致

クエリー・リライト, 19.3.10

外部表, 15.2.2 抽出、変換、ロード(ETL), 12.1

概要, 12.1

プロセス, 8.1

抽出

データファイル, 13.3.1

分散処理, 13.3.2

全体, 13.2.1

増分, 13.2.1.2

OCI, 13.3.1.2

オンライン, 13.2.2.1

概要, 13.1

物理, 13.2.2

Pro*C, 13.3.1.2

SQL*Plus, 13.3.1.1

F

ファクト表, 2.4, 2.4.1

スター型結合, 20.3

スター・クエリー, 20.3

ファクト, 11.1 FAST句, 9.3.7 高速リフレッシュ, 16.3.2

制限事項, 9.3.7.1

UNION ALLによる, 16.3.18

新機能, Preface ファイル

巨大, 3.2.2

ギャップ補完

データ, 22.5.6

FIRST_VALUE関数, 22.2.5.1 FIRST/LAST関数, 22.3.2 FORループ, 23.3.1 FORCE句, 9.3.7 外部キー

制約, 8.3.2

結合

スノーフレーク・スキーマ, 20.3.1

高頻度項目セット, 22.7.4 関数

分析, 23.3.5

COUNT, 7.1.3

CUME_DIST, 22.2.1.3

DENSE_RANK, 22.2.1.1

FIRST_VALUE, 22.2.5.1

FIRST/LAST, 22.3.2

GROUP_ID, 21.4.4

GROUPING, 21.4

GROUPING_ID, 21.4.3

LAG/LEAD, 22.2.4

LAST_VALUE, 22.2.5.1

線形回帰, 22.3.5

LISTAGG, 22.3.1

NTH_VALUE, 22.2.5.2

NTILE, 22.2.1.5

PERCENT_RANK, 22.2.1.4

RANK, 22.2.1.1

ランキング, 22.2.1

RATIO_TO_REPORT, 22.2.3.1

REGR_AVGX, 22.3.5.2

REGR_AVGY, 22.3.5.2

REGR_COUNT, 22.3.5.1

REGR_INTERCEPT, 22.3.5.3

REGR_SLOPE, 22.3.5.3

REGR_SXX, 22.3.5.5

REGR_SXY, 22.3.5.5

REGR_SYY, 22.3.5.5

レポート, 22.2.3

ROW_NUMBER, 22.2.1.6

WIDTH_BUCKET, 22.7.1, 22.7.1.1

ウィンドウ, 23.3.5

ウィンドウ, 22.2.2

G

GRANT文, 17.9.4.5

GROUP_ID関数, 21.4.4

グルーピング

互換性チェック, 19.1.3.3

条件, 19.7.6

GROUPING関数, 21.4

使用するとき, 21.4.2

GROUPING_ID関数, 21.4.3 GROUPING_SETS式, 21.5 GT GlossaryTitle, 用語集

H

階層的キューブ, 10.2.1, 21.10.1

SQLでの, 21.10

階層, 11.1

使用方法, 2.4.2.1

複数, 11.2.2

概要, 2.4.2.1, 2.4.2.1

ロールアップおよびドリルダウン, 11.1

高位限界

定義, 17.1.3.2

ヒント

EXPAND_GSET_TO_UNION, 19.3.8.2, 19.7.10.4

NOWRITE, 19.7.10.1

クエリー・リライト, 18.2, 19.7.10.1

REWRITE, 19.7.10.1

REWRITE_OR_ERROR, 19.7.10.2

ヒストグラム

ユーザー定義バケットを使用した作成, 22.7.3.1

HOTLOG_SOURCEチェンジ・ソース, 17.2.2.1 仮説ランク, 22.3.4

I

インポート

チェンジ・テーブル, 17.9.5, 17.9.5.3

非同期チェンジ・セット, 17.9.5.1

非同期チェンジ・テーブル, 17.9.5.1

ソース・テーブルへのデータ, 17.9.5.3

索引

ビットマップ索引, 7.1.4

ビットマップ結合, 7.1.5

Bツリー, 7.2

カーディナリティ, 7.1.2

NULL, 7.1.3

パーティション表, 7.1.4

initcdc.sqlスクリプト, 17.12 初期化パラメータ

JOB_QUEUE_PROCESSES, 16.3.9

OPTIMIZER_MODE, 16.3.11, 18.2.1

PARALLEL_MAX_SERVERS, 16.3.11

PGA_AGGREGATE_TARGET, 16.3.11

QUERY_REWRITE_ENABLED, 18.2, 18.2.1

QUERY_REWRITE_INTEGRITY, 18.2.1

STAR_TRANSFORMATION_ENABLED, 20.4.1

整合性制約, 8.1 無効化

マテリアライズド・ビュー, 10.4

項目セット

高頻度, 22.7.4

J

Java

チェンジ・データ・キャプチャで使用, 17.12

JOB_QUEUE_PROCESSES初期化パラメータ, 16.3.9 結合互換性, 19.1.3.1 結合

スター型結合, 20.3

スター・クエリー, 20.3

K

キー参照, 15.5.1

キー, 9.1.6, 20.3

L

LAG/LEAD関数, 22.2.4

LAST_VALUE関数, 22.2.5.1

レベル関係, 2.4.2.1

目的, 2.4.2.1.2

レベル, 2.4.2, 2.4.2.1.1 ディメンション内のレベル

スキップ, 11.2

線形回帰関数, 22.3.5 LISTAGG関数, 22.3.1 ローカル索引, 7.1.1, 7.1.4 ロギング

エラー, 15.4

論理設計, 3.1 ログ

マテリアライズド・ビュー, 9.3.9

参照表

「ディメンション表」を参照, 9.1.6, 9.1.6

低位限界

定義, 17.1.3.2

M

手動

リフレッシュ, 16.3.5

手動リフレッシュ

DBMS_MVIEWパッケージ, 16.3.5

マテリアライズド・ビュー・ログ, 9.3.9 マテリアライズド・ビュー

集計, 9.2.1

変更, 10.6

分析処理, 10.2

作成方法, 9.3.4

ステータスのチェック, 16.3.13

結合のみを含む, 9.2.2

作成, 9.3

キューブ, 10.2.1

データ・セグメントの圧縮, 9.3.3

デルタ結合, 19.1.3.1.3

削除, 9.4, 9.6

無効化, 10.4

ログ, 13.2.3.3

複数, 19.2.8

名前付け, 9.3.2

ネステッド・マテリアライズド・ビュー, 9.2.3

パーティション・チェンジ・トラッキング(PCT), 10.1.1

パーティション表, 16.4

パーティション化, 10.1

事前作成, 9.3

クエリー・リライト

ヒント, 18.2, 19.7.10.1

一致結合グラフ, 9.3.6

パラメータ, 18.2.1

権限, 18.2.4

依存、リフレッシュ, 16.3.8 リフレッシュ, 9.3.7, 16.3 すべてをリフレッシュ, 16.3.7 登録, 9.4 制限事項, 9.3.6.1 リライト

有効化, 18.2

スキーマ設計, 9.1.7 スキーマ設計のガイドライン, 9.1.7 セキュリティ, 10.5, 10.5 集合演算子, 10.2.4 記憶域の特性, 9.3.3 タイプ, 9.2 使用, 9.1 VPDを含む, 10.5.1 メジャー, 9.1.6, 20.3 MERGE文, 16.2.1

チェンジ・データ・キャプチャの制限, 17.10.1

MODEL句, 23.1

セル参照, 23.2.4

データ・フロー, 23.1.1

キーワード, 23.2.3

パラレル実行, 23.4.1

ルール, 23.2.5

監視

リフレッシュ, 16.3.12

貸付金の計算, 23.5 複数の階層, 11.2.2 複数のマテリアライズド・ビュー, 19.2.8 MV_CAPABILITIES_TABLE表, 9.7.1.2

N

ネステッド・マテリアライズド・ビュー, 9.2.3

リフレッシュ, 16.3.17

制限事項, 9.2.3.4

正味現在価値

計算, 23.5

NEVER句, 9.3.7 新機能, Preface 恒常的データ, 1.1.3 NOREWRITEヒント, 18.2, 19.7.10.1 NTH_VALUE関数, 22.2.5.2 NTILE関数, 22.2.1.5 NULL

索引, 7.1.3

O

ON COMMIT句, 9.3.7

ON DEMAND句, 9.3.7

OPERATION$

制御列, 17.9.4.2

最適化

クエリー・リライト

有効化, 18.2

ヒント, 18.2, 19.7.10.1

一致結合グラフ, 9.3.6

クエリー・リライト

権限, 18.2.4

オプティマイザ

リライト, 18.1

OPTIMIZER_MODE初期化パラメータ, 16.3.11, 18.2.1 ORA-31424エラー, 17.9.4.7 ORA-31496エラー, 17.9.4.7 Oracle Data Pump

チェンジ・データ・キャプチャで使用, 17.9.5

ORDER BY句, 9.3.8 外部結合

クエリー・リライト, 19.7.3

P

パッケージ

DBMS_ADVISOR, 9.1.4

DBMS_CDC_PUBLISH, 17.1.2

DBMS_CDC_SUBSCRIBE, 17.1.2

DBMS_DIMENSION, 11.3.2

DBMS_ERROR, 15.4.2.2

DBMS_ERRORLOG, 15.4.2.2, 15.5.3

DBMS_JOB, 17.9.4.6

DBMS_MVIEW, 9.7.1.1, 16.3

DBMS_STATS, 19.1.1

段落タグ

GT GlossaryTitle, 用語集

パラレルDML

ビットマップ索引, 7.1.1

パラレル実行, 6.1 パラレル問合せ

ビットマップ索引, 7.1.1

PARALLEL_MAX_SERVERS初期化パラメータ, 16.3.11 パラレル化, 6.1 パーティション・チェンジ・トラッキング(PCT), 10.1.1, 16.4.1, 19.2.7

リフレッシュ, 16.3.3

Pmarker, 19.2.7.4

パーティション外部結合, 22.5 パーティション表

マテリアライズド・ビュー, 16.4

パーティション化, 13.2.3.2

データ, 5.1

データ・ウェアハウスにおける, 5

マテリアライズド・ビュー, 10.1

事前作成表, 10.1.3

パーティション

ビットマップ索引, 7.1.4

PERCENT_RANK関数, 22.2.1.4 PGA_AGGREGATE_TARGET初期化パラメータ, 16.3.11 物理設計, 3.1

構造, 3.2.1

ピボット, 15.5.4, 15.5.4, 22.4

操作, 22.4

計画

スター型変換, 20.4.2.2

PL/SQLプロシージャ

DBMS_CDC_PUBLISH_DROP_CHANGE_TABLE, 17.9.4.7

DBMS_CDC_PUBLISH.PURGE, 17.9.4.6, 17.9.4.6

DBMS_CDC_PUBLISH.PURGE_CHANGE_SET, 17.9.4.6

DBMS_CDC_PUBLISH.PURGE_CHANGE_TABLE, 17.9.4.6

DBMS_CDC_SUBSCRIBE.PURGE_WINDOW, 17.9.4.6

Pmarker

PCT, 19.2.7.4

事前作成マテリアライズド・ビュー, 9.3 必要な権限

変更データの公開用, 17.6.1.1

パブリケーション

定義, 17.1.3.1

パブリッシャ

関連するコンポーネント, 17.1.3.1

定義, 17.1.3

ソース・テーブルの判断, 17.1.3.1

ビューを読み取る権限, 17.5

目的, 17.1.3.1

チェンジ・テーブルのパーティション化プロパティ, 17.9.4.1

タスク, 17.1.3.1

公開

非同期AutoLogモード

手順例, 17.7.4

非同期HotLogモード

手順例, 17.7.2

同期モード

手順例, 17.7.1

変更データの公開

準備, 17.6

必要な権限, 17.6.1.1

チェンジ・テーブルのパージ

自動的, 17.9.4.6

指定, 17.9.4.6

指定のチェンジ・セット, 17.9.4.6

ステージング・データベース上, 17.9.4.6

パブリッシャ, 17.9.4.6

サブスクライバ, 17.9.4.6

データの削除, 16.2.3

Q

問合せ

スター・クエリー, 20.3

問合せデルタ結合, 19.1.3.1.2 クエリー・リライト

高度, 19.4

行われるチェック, 19.1.3

制御, 18.2.2

精度, 18.2.3

デート・フォールディング, 19.3.5

有効化, 18.2, 18.2

ヒント, 18.2, 19.7.10.1

一致結合グラフ, 9.3.6

方法, 19.1

パラメータ, 18.2.1

権限, 18.2.4

制限事項, 9.3.6.2

同等化を使用, 19.4

GROUP BY拡張機能を使用, 19.3.8.1

ネステッド・マテリアライズド・ビューを使用, 19.3.1

PCTの使用, 19.2.7

VPD, 10.5.1.1

実行条件, 18.1.1

バインド変数, 19.3.11

DBMS_MVIEWパッケージ, 19.6.2

式の一致, 19.3.10

インライン・ビューを使用, 19.3.2

部分的に失効したマテリアライズド・ビューを使用, 19.3.10.1

自己結合を使用, 19.3.4

集合演算子を含むマテリアライズド・ビュー, 19.3.7

ビューの制約, 19.3.6

問合せツール

説明, 1.3.1.9

QUERY_REWRITE_ENABLED初期化パラメータ, 18.2, 18.2.1 QUERY_REWRITE_INTEGRITY初期化パラメータ, 18.2.1

R

RANK関数, 22.2.1.1

ランキング関数, 22.2.1

RATIO_TO_REPORT関数, 22.2.3.1

リカバリ

非同期チェンジ・セットのキャプチャ・エラー, 17.9.2.4

REDOログ・ファイル

アーカイブ

非同期チェンジ・データ・キャプチャ, 17.11.1

チェンジ・データ・キャプチャで必要なくなったものの判別, 17.11.1 参照表

「ディメンション表」を参照, 9.1.6

リフレッシュ

監視, 16.3.12

オプション, 9.3.7

パーティション・チェンジ・トラッキング(PCT), 16.3.3

スケジューリング, 16.3.14

UNION ALLによる, 16.3.18

リフレッシュ

マテリアライズド・ビュー, 16.3

ネステッド・マテリアライズド・ビュー, 16.3.17

パーティション化, 16.1

REGR_AVGX関数, 22.3.5.2 REGR_AVGY関数, 22.3.5.2 REGR_COUNT関数, 22.3.5.1 REGR_INTERCEPT関数, 22.3.5.3 REGR_R2関数, 22.3.5.4 REGR_SLOPE関数, 22.3.5.3 REGR_SXX関数, 22.3.5.5 REGR_SXY関数, 22.3.5.5 REGR_SYY関数, 22.3.5.5 RELY制約, 8.3.3 削除

ソース・データベースのチェンジ・データ・キャプチャ, 17.12

reporting関数, 22.2.3 制限

高速リフレッシュ, 9.3.7.1

ネステッド・マテリアライズド・ビュー, 9.2.3.4

クエリー・リライト, 9.3.6.2

結果セット, 20.4.2 REVOKE文, 17.9.4.5 REWRITEヒント, 18.2, 19.7.10.1, 19.7.10.1 REWRITE_OR_ERRORヒント, 19.7.10.2 リライト

ヒント, 19.7.10.1

パラメータ, 18.2.1

権限, 18.2.4

問合せ最適化

ヒント, 18.2, 19.7.10.1

一致結合グラフ, 9.3.6

rmcdc.sqlスクリプト, 17.12 階層のロールアップ, 11.1 ROLLUP, 21.2

連結, 21.10.1

部分的, 21.2.3

使用するとき, 21.2.1

ルート・レベル, 2.4.2.1.1 ROW_ID$

制御列, 17.9.4.2

ROW_NUMBER関数, 22.2.1.6 RSID$

制御列, 17.9.4.2

ルール

MODEL句, 23.2.5

SQLモデリング, 23.2.5

評価の順序, 23.2.6

S

スキーマ・レベルのエクスポート操作, 17.9.5.1

スキーマ・レベルのインポート操作, 17.9.5.1

スキーマ, 20.1

マテリアライズド・ビューのスキーマ・デザイン、ガイドライン, 9.1.7

スノーフレーク, 2.3

スター, 2.3

第3正規形, 20.1, 20.2

スクリプト

チェンジ・データ・キャプチャ用initcdc.sql, 17.12

チェンジ・データ・キャプチャ用rmcdc.sql, 17.12

SELECT_CATALOG_ROLE権限, 17.5, 17.6.1.1 集合演算子

マテリアライズド・ビュー, 10.2.4

連立方程式, 23.5 SKIP WHEN NULL句, 11.2 ディメンション内のレベルのスキップ, 11.2 スノーフレーク・スキーマ, 20.3.1

複合問合せ, 20.3.1

ソース・データベース

定義, 17.1.3.1

ソース・システム, 13.1 ソース・テーブル

チェンジ・データ・キャプチャ用のインポート, 17.9.5.3

チェンジ・テーブルによる参照, 17.9.4.1

SOURCE_COLMAP$

制御列, 17.9.4.2

解釈, 17.9.4.3

スパースなデータ

データの稠密化, 22.5.2

SQLモデリング, 23.1

セル参照, 23.2.4

キーワード, 23.2.3

評価の順序, 23.2.6

パフォーマンス, 23.4

ルール, 23.2.5

規則および制限事項, 23.3.7

ステージング

エリア, 1.2.2

データベース, 9.1

ファイル, 9.1

ステージング・データベース

定義, 17.1.3.1

STALE_TOLERATEDモード, 18.2.3 スター型結合, 20.3 スター・クエリー, 20.3

スター型変換, 20.4.2

スター・スキーマ

メリット, 2.3.1

ファクト表の定義, 2.4.1.1

ディメンショナル・モデル, 2.3.1, 20.3

スター型変換, 20.4.2

制限事項, 20.4.2.6

STAR_TRANSFORMATION_ENABLED初期化パラメータ, 20.4.1 統計情報, 19.7.9 Streams適用プロセス

非同期チェンジ・データ・キャプチャ, 17.6.4

Streams取得プロセス

非同期チェンジ・データ・キャプチャ, 17.6.4

ストライプ化, 4.1.2 サブスクライバ・ビュー

定義, 17.1.3.2

DML変更を発生順に戻す, 17.9.4.2, 17.9.4.2

サブスクライバ

チェンジ・テーブルへのアクセス, 17.9.4.5

ALL_PUBLISHED_COLUMNSビュー, 17.9.6

関連するコンポーネント, 17.1.3.2

チェンジ・テーブルへのアクセスの制御, 17.9.4.5

定義, 17.1.3

DMLアクセス, 17.9.4.5

権限, 17.1.3.2

目的, 17.1.3.2

サブスクライバ・ビューからの変更データの取出し, 17.1.3.2

タスク, 17.1.3.2

サブスクライブ

手順例, 17.8

サブスクリプション・ウィンドウ

定義, 17.1.3.2

サブスクリプション

チェンジ・テーブルに対する変更, 17.9.6

定義, 17.1.3.2

SQL文のDROP USER CASCADEの影響, 17.9.4.7

サマリー管理

コンポーネント, 9.1.5

サマリー表, 2.4.1 サプリメンタル・ロギング

非同期チェンジ・データ・キャプチャ, 17.11.2

非同期チェンジ・データ・キャプチャ, 17.2.2

SYNC_SET事前定義チェンジ・セット, 17.3 SYNC_SOURCEチェンジ・ソース, 17.2.1 同期チェンジ・データ・キャプチャ

チェンジ・セット, 17.3

同期チェンジ・セット

無効化, 17.9.2.2

有効化, 17.9.2.2

同期公開

待機時間, 17.6.2

ステージング・データベースの場所, 17.6.2

要件, 17.7.1

データベース初期化パラメータの設定, 17.6.3.1

ソース・データベースのパフォーマンスへの影響, 17.6.2

SYS_NC_OID$

制御列, 17.9.4.2

T

表区別, 17.1.1

表のパーティション化

パブリッシャ, 17.9.4.1

表

ディテール表, 9.1.6

ディメンション表(参照表), 9.1.6

ディメンション

スター・クエリー, 20.3

外部, 15.2.2 ファクト表, 9.1.6

スター・クエリー, 20.3

参照, 20.3 表領域

チェンジ・データ・キャプチャのパブリッシャのデフォルト指定, 17.6.1.2

表領域

チェンジ・テーブル, 17.9.4.1

トランスポータブル, 13.2.2.2, 14.2.3, 14.2.3.2

TARGET_COLMAP$

制御列, 17.9.4.2

解釈, 17.9.4.3

テキストの一致, 19.2.1

クエリー・リライト, 19.7.4

第3正規形

問合せ, 20.2.1

スキーマ, 20.2

時系列の計算, 22.6 TIMESTAMP$

制御列, 17.9.4.2

タイムスタンプ, 13.2.3.1 変換, 15.1

使用例, 15.5

SQLおよびPL/SQL, 15.3.1

SQL*Loader, 15.2.1

トランスポータブル表領域, 13.2.2.2, 14.2.3, 14.2.3.2 転送

定義, 14.1

分散処理, 14.2.2

フラット・ファイル, 14.2.1

トリガー, 13.2.3.3

チェンジ・データ・キャプチャでインストールされる, 17.12

TRUSTEDモード, 18.2.3

U

巨大なファイル, 3.2.2

一意

制約, 8.3.1

識別子, 2.2, 3.2

更新頻度, 9.1.9 UPDATE文, 23.2.8 更新ウィンドウ, 9.1.9 UPSERT ALL文, 23.2.8 UPSERT文, 23.2.8 USERNAME$

制御列, 17.9.4.2

V

ディメンションの妥当性チェック, 11.5

ビューの制約, 8.3.7, 19.3.6

VPD

マテリアライズド・ビュー, 10.5.1

マテリアライズド・ビューに関する制限, 10.5.1.2

W

WIDTH_BUCKET関数, 22.7.1, 22.7.1.1

ウィンドウ関数, 23.3.5

ウィンドウ関数, 22.2.2

X

XIDSEQ$

制御列, 17.9.4.2

XIDSLT$

制御列, 17.9.4.2

XIDUSN$

制御列, 17.9.4.2

オラクル社の法律上の注意点について

コピーライトについて

Copyright © 1994-2013, Oracle and/or its affiliates.All rights reserved.

商標と登録商標について

OracleおよびJavaはOracle Corporationおよびその関連企業の登録商標です。その他の名称は、それぞれの所有者の商標または登録商標です。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

ライセンス制約の保証と結果的に生じる損害の免責

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

保証免責

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクル社までご連絡ください。

制限付権利

このソフトウェアまたは関連ドキュメントを、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供する場合は、次の通知が適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations.As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs.No other rights are granted to the U.S. Government.

危険な用途への使用について

このソフトウェアもしくはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアもしくはハードウェアは、危険が伴うアプリケーション（人的傷害を発生させる可能性があるアプリケーションを含む）への用途を目的として開発されていません。このソフトウェアもしくはハードウェアを危険が伴うアプリケーションで使用する際、安全に使用するために、適切な安全装置、バックアップ、冗長性（redundancy）、その他の対策を講じることは使用者の責任となります。このソフトウェアもしくはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、オラクル社およびその関連会社は一切の責任を負いかねます。

第三者のコンテンツ、製品、サービスに対する免責事項

このソフトウェアまたはハードウェア、そしてドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。オラクル社およびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。オラクル社およびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても一切の責任を負いかねます。

ドラフトのアルファ版およびベータ版ドキュメントについて

このドキュメントが試作版の場合:

このドキュメントは試作版であり、デモおよび暫定使用のみを目的としたものです。このソフトウェアを使用するハードウェアに限定するものではありません。オラクル社およびその関連会社は、このドキュメントに関して一切の責任を負わず、いかなる保証もいたしません。また、このドキュメントを使用したことによって損失、費用、あるいは損害が発生しても、一切の責任を負いかねます。