...

Embarcadero® InterBase XE3™ 埋め込み SQL ガイド

by user

on
Category: Documents
3

views

Report

Comments

Transcript

Embarcadero® InterBase XE3™ 埋め込み SQL ガイド
製品ドキュメント
Embarcadero® InterBase XE3™
埋め込み SQL ガイド
バージョン: Update 4
リリース日: 2014 年 3 月
2©2014 Embarcadero Technologies, Inc. Embarcadero、 Embarcadero Technologies の ロ ゴ、 お
よ びすべての Embarcadero Technologies の製品名ま たはサービ ス名は、 Embarcadero Technologies, Inc. の商標ま たは登録商標です。 その他の商標名に関す る 権利はすべてその所有者に帰属
し ます。
本ソフトウェア / ドキュメントには、Embarcadero Technologies, Inc. の機密情報を含んでいます。 こ
れはライセンス契約の基に提供され、 契約には使用や情報開示における制限が含まれており、 また、 著作権法
によっても保護されています。 ソフトウェアのリバース エンジニアリングは禁じられています。
Embarcadero Technologies, Inc. は、 アプリケーション開発者やデータベース プロフェッショナルに向け、
使用するプラットフォームやプログラミング言語に関係なく、 最適なシステム設計、 より高速な構築、 より高品質
な実行を実現するツールを提供する、 先進ベンダーです。 「フォーチュン 100」 のうち 90 以上の企業、 そして
世界で 300 万以上の活発なコミュニティがエンバカデロの製品を採用し、 生産性の向上、 コストの削減、 変更
管理や互換性管理の簡素化、 技術革新を実現しています。 主要製品には次のものがあります : Embarcadero® Change Manager™、 CodeGear™ RAD Studio、 DBArtisan®、 Delphi®、
ER/Studio®、 JBuilder® and Rapid SQL®。 1993 年設立のエンバカデロは、 サンフランシスコに本社を
置き、 世界各国に支社を展開しています。 エンバカデロについては、 http://www.embarcadero.com を
ご覧ください。
2014 年 3 月 10 日
目次
表 . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
複数のデー タ ベースへの接続 . . . . . . . . 3-11
接続中のエ ラ ー処理. . . . . . . . . . . . . . 3-12
デー タ ベース キ ャ ッ シ ュ バ ッ フ ァ の設定. . 3-13
開かれてい る デー タ ベースへのア ク セ ス . . . . . 3-14
テーブル名の識別 . . . . . . . . . . . . . . . . . . . . . . . . . 3-14
デー タ ベース を閉 じ る . . . . . . . . . . . . . . . . . . . . . 3-15
DISCONNECT でデー タ ベース を閉 じ る . . 3-15
COMMIT と ROLLBACK に よ り デー タ ベース を
閉 じ る . . . . . . . . . . . . . . . . . . . . . 3-16
図 . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
第1章
1. 埋め込み SQL ガイドの使い方
対象読者 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-1
こ のガ イ ド の内容 . . . . . . . . . . . . . . . . . . . . . . . . . . 1-2
第2章
2. アプリケーションの必要条件
第4章
すべてのアプ リ ケーシ ョ ンの必要条件 . . . . . . . . 2-1
SQL アプ リ ケーシ ョ ン を移植す る 場合の注意 .
2-2
DSQL アプ リ ケーシ ョ ン を移植す る 場合の注意
2-2
ホ ス ト 変数の宣言 . . . . . . . . . . . . . . . 2-2
デー タ ベース の宣言 と 初期化 . . . . . . . . . . . . . . . . 2-5
SET DATABASE の使用 . . . . . . . . . . . . 2-5
CONNECT の使用 . . . . . . . . . . . . . . . 2-6
デー タ ベース が 1 つの場合 . . . . . . . . . . 2-6
SQL 文. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-7
エ ラ ー処理 と 復旧 . . . . . . . . . . . . . . . . . . . . . . . . . . 2-7
ト ラ ンザ ク シ ョ ンの終了 . . . . . . . . . . . . . . . . . . . . 2-8
変更の保存 . . . . . . . . . . . . . . . . . . . 2-8
変更の取 り 消 し . . . . . . . . . . . . . . . . . 2-8
デー タ ベース を閉 じ る . . . . . . . . . . . . . . . . . . . . . . 2-9
DSQL の必要条件 . . . . . . . . . . . . . . . . . . . . . . . . . . 2-9
XSQLDA の宣言 . . . . . . . . . . . . . . . . 2-10
DSQL に関す る 制限事項 . . . . . . . . . . . . . . . . . . . 2-11
デー タ ベース ハン ド ルの使い方 . . . . . . . 2-11
有効なデー タ ベース の使い方 . . . . . . . . . 2-12
ト ラ ンザ ク シ ョ ン名の使い方 . . . . . . . . . 2-12
プ ロ グ ラ ムの前処理 . . . . . . . . . . . . . . . . . . . . . . . 2-13
4. トランザクションの操作
デフ ォ ル ト ト ラ ンザ ク シ ョ ンの起動 . . . . . . . . . . 4-2
SET TRANSACTION なしの起動 . . . . . . . . 4-2
SET TRANSACTION によるデフォルトトランザク
ションの起動 . . . . . . . . . . . . . . . . . . . 4-3
名前付 き ト ラ ンザ ク シ ョ ンの起動 . . . . . . . . . . . . 4-4
ト ラ ンザ ク シ ョ ンの命名 . . . . . . . . . . . . 4-5
SET TRANSACTION での動作の指定 . . . . 4-7
デー タ 操作文での ト ラ ンザ ク シ ョ ン名の扱い . 4-20
ト ラ ンザ ク シ ョ ンの終了 . . . . . . . . . . . . . . . . . . . 4-21
COMMIT の使い方. . . . . . . . . . . . . . . 4-22
ROLLBACK の使い方 . . . . . . . . . . . . . 4-25
複数の ト ラ ンザ ク シ ョ ンの使い方 . . . . . . . . . . . 4-26
デフ ォ ル ト の ト ラ ンザ ク シ ョ ン . . . . . . . 4-27
カー ソ ルの扱い . . . . . . . . . . . . . . . . 4-27
複数の ト ラ ンザ ク シ ョ ン を使用す る 場合の処理
例 . . . . . . . . . . . . . . . . . . . . . . . 4-28
DSQL での複数の ト ラ ンザ ク シ ョ ンの使い方 4-29
“?” に よ る ト ラ ンザ ク シ ョ ンの動作の変更 . 4-30
第5章
5. データ定義文
メ タ デー タ の作成 . . . . . . . . . . . . . . . . . . . . . . . . . . 5-2
メ タ デー タ 名. . . . . . . . . . . . . . . . . . . 5-2
デー タ ベース の作成. . . . . . . . . . . . . . . 5-3
ド メ イ ンの作成 . . . . . . . . . . . . . . . . . 5-4
テーブルの作成 . . . . . . . . . . . . . . . . . 5-5
ビ ュ ーの作成. . . . . . . . . . . . . . . . . . . 5-8
イ ンデ ッ ク ス の作成. . . . . . . . . . . . . . 5-10
ジ ェ ネ レー タ の作成. . . . . . . . . . . . . . 5-11
メ タ デー タ の削除 . . . . . . . . . . . . . . . . . . . . . . . . . 5-12
イ ンデ ッ ク ス の削除. . . . . . . . . . . . . . 5-12
ビ ュ ーの削除. . . . . . . . . . . . . . . . . . 5-13
テーブルの削除 . . . . . . . . . . . . . . . . 5-13
メ タ デー タ の変更 . . . . . . . . . . . . . . . . . . . . . . . . . 5-14
第3章
3. データベースの操作
デー タ ベース の宣言 . . . . . . . . . . . . . . . . . . . . . . . . 3-1
複数のデー タ ベース の宣言 . . . . . . . . . . 3-2
前処理時 と 実行時のデー タ ベース . . . . . . 3-4
SET DATABASE のデー タ ベースハン ド ルの ス
コ ープ . . . . . . . . . . . . . . . . . . . . . 3-5
接続キ ャ ラ ク タ セ ッ ト の指定 . . . . . . . . . . . . . . . . 3-6
デー タ ベース を開 く . . . . . . . . . . . . . . . . . . . . . . . . 3-6
CONNECT 文の基本形 . . . . . . . . . . . . 3-7
その他の CONNECT 構文 . . . . . . . . . . . 3-11
3
テーブルの修正 . . . . . . . . . . . . . . . . . 5-14
ビ ュ ーの修正 . . . . . . . . . . . . . . . . . . 5-17
イ ンデ ッ ク ス の修正 . . . . . . . . . . . . . . 5-18
相関的なサブ ク エ リ ー . . . . . . . . . . . .
デー タ の挿入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
VALUES に よ る デー タ の挿入 . . . . . . . .
SELECT に よ る デー タ の挿入 . . . . . . . .
NULL を含んだ新規行の追加 . . . . . . . .
ビ ュ ーを使っ たデー タ の挿入 . . . . . . . .
INSERT でのトランザクション名の指定 . . . .
デー タ の更新 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
複数行の更新. . . . . . . . . . . . . . . . . .
UPDATE による列への NULL の設定 . . . . .
ビ ュ ーを使っ た更新. . . . . . . . . . . . . .
UPDATE でのトランザクション名の指定 . . .
デー タ の削除 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
複数行の削除. . . . . . . . . . . . . . . . . .
ビ ュ ーを使っ た削除. . . . . . . . . . . . . .
DELETE でのトランザクション名の指定 . . .
第6章
6. データ処理
サポー ト す る デー タ 型 . . . . . . . . . . . . . . . . . . . . . . 6-2
SQL 式. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-4
式での文字列演算子の使い方 . . . . . . . . . 6-6
式での算術演算子の使い方 . . . . . . . . . . 6-6
式での論理演算子の使い方 . . . . . . . . . . 6-7
式での比較演算子の使い方 . . . . . . . . . . 6-8
演算子の優先順位 . . . . . . . . . . . . . . . 6-15
CAST() に よ る デー タ 型の変換 . . . . . . . . 6-17
UPPER() に よ る テ キ ス ト デー タ の大文字変更 .
6-18
SELECT に よ る デー タ の取 り 出 し . . . . . . . . . . . 6-19
SELECT での取 り 出 し 列の指定 . . . . . . . 6-21
ト ラ ンザ ク シ ョ ン名の指定 . . . . . . . . . . 6-23
INTO での変数の指定 . . . . . . . . . . . . . . 6-24
FROM でのテーブルの指定 . . . . . . . . . . . 6-24
WHERE での行の絞 り 込み . . . . . . . . . . 6-27
ORDER BY による行のソート . . . . . . . . . 6-29
GROUP BY に よ る 複数行のグループ化 . . . 6-31
HAVING による複数行のグループ化 . . . . . . 6-32
ROWS に よ る 結果セ ッ ト の限定 . . . . . . . 6-33
UNION に よ る テーブルの付加 . . . . . . . . 6-34
PLAN に よ る ク エ リ ープ ラ ンの指定 . . . . . 6-34
1 行の取 り 出 し . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-35
複数行の取 り 出 し . . . . . . . . . . . . . . . . . . . . . . . . . 6-36
カー ソ ルの宣言 . . . . . . . . . . . . . . . . . 6-37
カー ソ ルのオープン . . . . . . . . . . . . . . 6-38
カー ソ ルに よ る 行の取 り 出 し . . . . . . . . . 6-38
カー ソ ルの ク ロ ーズ . . . . . . . . . . . . . . 6-41
カー ソ ルに よ る 行の取 り 出 し のプ ロ グ ラ ム例 .
6-41
NULL 値が格納 さ れてい る 行の取 り 出 し . . 6-42
ビ ュ ーに よ る 行の取 り 出 し . . . . . . . . . . 6-44
DSQL での複数行の取 り 出 し . . . . . . . . . . . . . . . 6-44
DSQL カー ソ ルの宣言 . . . . . . . . . . . . . 6-45
DSQL カー ソ ルのオープン . . . . . . . . . . 6-46
DSQL カー ソ ルに よ る 複数行の取 り 出 し . . 6-46
テーブルの結合 . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-46
結合で使用す る 列の選択 . . . . . . . . . . . 6-47
内部結合 . . . . . . . . . . . . . . . . . . . . . 6-47
外部結合 . . . . . . . . . . . . . . . . . . . . . 6-50
ネ ス ト さ れた結合 . . . . . . . . . . . . . . . 6-52
サブ ク エ リ ー . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-52
基本的なサブ ク エ リ ー . . . . . . . . . . . . . 6-53
6-54
6-55
6-56
6-56
6-57
6-59
6-60
6-61
6-62
6-65
6-65
6-66
6-67
6-67
6-70
6-70
第7章
7. 日付と時刻
現在の日付 と 時刻の情報をデー タ ベース に問い合
わせ る . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-2
現在の日付 と 時刻の取得 . . . . . . . . . . . . 7-2
日付時刻情報の取 り 出 し . . . . . . . . . . . . 7-2
日付 と 時刻の取 り 出 し . . . . . . . . . . . . . . . . . . . . . . 7-3
入力のための日付の書式 . . . . . . . . . . . . . . . . . . . . 7-4
日付 と 時刻の挿入 . . . . . . . . . . . . . . . . . . . . . . . . . . 7-5
日付 と 時刻の更新 . . . . . . . . . . . . . . . . . . . . . . . . . . 7-6
CAST() に よ る 日付 と 時刻の変換 . . . . . . . . . . . . 7-6
SQL デー タ 型か ら 日付時刻デー タ 型へのキ ャ
ス ト . . . . . . . . . . . . . . . . . . . . . . . 7-7
日付時刻デー タ 型か ら 他の SQL デー タ 型への
キ ャ ス ト . . . . . . . . . . . . . . . . . . . . 7-8
日付 リ テ ラ ルの使い方 . . . . . . . . . . . . . . . . . . . . . . 7-9
日付お よ び時刻デー タ 型の加算 と 減算 . . . . . . . 7-10
日付 と 時刻の比較 . . . . . . . . . . . . . . . . . . . . . . . . . .7-11
日付お よ び時刻デー タ 型を集計関数で使用す る . . .
7-11
第8章
8.BLOB データの操作
BLOB の概要 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-1
BLOB デー タ の格納方法 . . . . . . . . . . . . . . . . . . . 8-2
BLOB サブ タ イ プ. . . . . . . . . . . . . . . . 8-2
BLOB のデー タ ベース上での格納形式. . . . 8-3
BLOB のセグ メ ン ト 長 . . . . . . . . . . . . . 8-4
DECLARE CURSOR でのセグ メ ン ト 長の変更
8-5
SQL での BLOB デー タ へのア ク セ ス . . . . . . . . 8-5
4
BLOB デー タ の取 り 出 し . . . . . . . . . . . 8-6
BLOB デー タ の挿入 . . . . . . . . . . . . . . 8-8
BLOB デー タ の更新 . . . . . . . . . . . . . . 8-9
BLOB デー タ の削除 . . . . . . . . . . . . . . 8-10
API 呼び出 し に よ る BLOB デー タ へのア ク セ ス .
EVENT WAIT に よ る イ ベン ト の通知の待機 . . .11-4
イ ベン ト に対す る 応答 . . . . . . . . . . . . . . . . . . . . . .11-5
第 12 章
12. エラー処理と回復
8-11
標準のエ ラ ー処理 . . . . . . . . . . . . . . . . . . . . . . . . . 12-1
WHENEVER 文 . . . . . . . . . . . . . . . . 12-2
SQLCODE の直接検査 . . . . . . . . . . . . 12-4
WHENEVER 文 と 直接検査の組み合わせ . 12-6
エ ラ ー処理のガ イ ド ラ イ ン. . . . . . . . . . 12-7
InterBase 独自のエ ラ ー処理 . . . . . . . . . . . . . . . 12-8
エ ラ ー メ ッ セージの表示 . . . . . . . . . . . 12-8
SQL エ ラ ー メ ッ セージの取 り 込み . . . . . 12-9
InterBase エ ラ ー メ ッ セージの取 り 込み . 12-10
InterBase エ ラ ー コ ー ド の処理 . . . . . . 12-12
BLOB デー タ の フ ィ ル タ リ ン グ . . . . . . . . . . . . . 8-11
標準 InterBase フ ィ ル タ . . . . . . . . . . . . 8-12
外部 BLOB フ ィ ル タ . . . . . . . . . . . . . 8-12
外部 BLOB フ ィ ル タ の作成. . . . . . . . . . . . . . . . . 8-14
フ ィ ル タ の種類 . . . . . . . . . . . . . . . . . 8-14
読み取 り 専用フ ィ ル タ と 書 き 込み専用フ ィ ル タ
8-14
フ ィ ル タ 関数の定義 . . . . . . . . . . . . . . 8-14
第9章
9. 配列の使い方
第 13 章
配列の作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-1
多次元配列 . . . . . . . . . . . . . . . . . . . 9-2
添字範囲の指定 . . . . . . . . . . . . . . . . . 9-2
配列へのア ク セ ス . . . . . . . . . . . . . . . . . . . . . . . . . . 9-3
配列か ら のデー タ の取 り 出 し . . . . . . . . . 9-4
配列へのデー タ の挿入 . . . . . . . . . . . . . 9-4
配列ス ラ イ ス のデー タ の取 り 出 し . . . . . . 9-5
配列ス ラ イ ス のデー タ の更新 . . . . . . . . . 9-6
検索条件での配列要素の評価 . . . . . . . . . 9-8
変数に よ る 配列の添字の指定 . . . . . . . . . 9-8
配列 と 算術式 . . . . . . . . . . . . . . . . . . 9-8
13. 動的 SQL
DSQL プ ロ グ ラ ミ ン グプ ロ セ ス の概要 . . . . . . 13-1
DSQL に関する 制限事項 . . . . . . . . . . . . . . . . . . 13-1
デー タ ベースへのア ク セ ス . . . . . . . . . . 13-2
ト ラ ンザ ク シ ョ ンの操作 . . . . . . . . . . . 13-3
デー タ ベース の作成. . . . . . . . . . . . . . 13-4
BLOB デー タ の処理 . . . . . . . . . . . . . 13-4
配列デー タ の処理 . . . . . . . . . . . . . . . 13-4
DSQL アプ リ ケーシ ョ ンの記述 . . . . . . . . . . . . 13-5
DSQL で処理可能な SQL 文 . . . . . . . . 13-5
SQL 文の文字列 . . . . . . . . . . . . . . . . 13-6
SQL 文の文字列の値パ ラ メ ー タ . . . . . . 13-6
XSQLDA. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13-7
XSQLDA の フ ィ ール ド . . . . . . . . . . . . 13-9
XSQLVAR の フ ィ ール ド . . . . . . . . . . . 13-9
入力デス ク リ プ タ . . . . . . . . . . . . . . .13-11
出力デス ク リ プ タ . . . . . . . . . . . . . . .13-11
XSQLDA_LENGTH マ ク ロ の使い方 . . 13-12
SQL デー タ 型マ ク ロ 定数 . . . . . . . . . 13-12
可変長文字列デー タ 型の操作 . . . . . . . 13-14
NUMERIC と DECIMAL のデー タ 型の扱い 13-15
デー タ 型の強制変換. . . . . . . . . . . . . 13-15
数値デー タ の配置 (ア ラ イ ン メ ン ト ). . . 13-16
DSQL のプ ロ グ ラ ミ ン グ手法 . . . . . . . . . . . . . 13-17
手法 1 : パ ラ メ ー タ のない非 ク エ リ ー SQL 文 .
13-17
手法 2 : パ ラ メ ー タ のあ る 非 ク エ リ ー SQL 文 .
13-18
手法 3 : パ ラ メ ー タ のない ク エ リ ー SQL 文. . .
13-21
第 10 章
10. ストアドプロシージャ
ス ト ア ド プ ロ シージ ャ の使い方 . . . . . . . . . . . . . 10-2
ス ト ア ド プ ロ シージ ャ と ト ラ ンザ ク シ ョ ン . 10-2
ス ト ア ド プ ロ シージ ャ と セキ ュ リ テ ィ . . . 10-2
選択プ ロ シージ ャ の使い方 . . . . . . . . . . . . . . . . . 10-3
選択プ ロ シージ ャ の呼び出 し . . . . . . . . . 10-4
カー ソ ル宣言での選択プ ロ シージ ャ の使用 . 10-4
実行可能プ ロ シージ ャ の使い方 . . . . . . . . . . . . . 10-4
実行可能プ ロ シージ ャ の実行 . . . . . . . . . 10-5
DSQL アプ リ ケーシ ョ ンでの ス ト ア ド プ ロ シー
ジ ャ の実行 . . . . . . . . . . . . . . . . . . 10-5
第 11 章
11. イベントの操作
InterBase の イ ベン ト メ カ ニズ ム . . . . . . . . . . . . 11-1
イ ベン ト の発生 . . . . . . . . . . . . . . . . . . . . . . . . . . . 11-2
イ ベン ト と の関係の登録 . . . . . . . . . . . . . . . . . . . 11-3
複数の イ ベン ト と の関係の登録 . . . . . . . . . . . . . 11-4
5
手法 4 : パ ラ メ ー タ のあ る ク エ リ ー SQL 文 . .
13-26
フ ァ イ ル拡張子を用いた言語の指定 . . . .
ソ ース フ ァ イ ルの指定 . . . . . . . . . . . .
コ ンパ イ ル と リ ン ク . . . . . . . . . . . . . . . . . . . . . . .
Microsoft Windows の場合 . . . . . . . . .
Solaris の場合 . . . . . . . . . . . . . . . . .
Ada プ ロ グ ラ ムの コ ンパ イ ル . . . . . . . .
UNIX 上での リ ン ク . . . . . . . . . . . . .
第 14 章
14. 前処理、コンパイル、リンク
前処理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14-1
gpre の使用 . . . . . . . . . . . . . . . . . . . 14-1
gpre ク ラ イ ア ン ト ダ イ ア レ ク ト の設定 . . . 14-4
6
14-5
14-5
14-7
14-7
14-8
14-8
14-8
表
1.1 『埋め込み SQL ガイド』の構成 . . . . . 1-2
3.1 CONNECT の構文のまとめ . . . . . . . . 3-11
4.1 トランザクション管理の SQL 文 . . . . 4-1
4.2 デフォルトトランザクションのデフォルト動
作 . . . . . . . . . . . . . . . . . . . . . . . 4-3
4.3 SET TRANSACTION のパラメータ . . . . 4-8
4.4 ISOLATION LEVEL のオプション . . . . 4-10
4.5 InterBase でのトランザクションの競合の
管理. . . . . . . . . . . . . . . . . . . . . . 4-12
4.6 排他レベルと SELECT および UPDATE との
関係. . . . . . . . . . . . . . . . . . . . . . 4-16
4.7 RESERVING 句を使用したテーブル予約のオ
プション . . . . . . . . . . . . . . . . . . 4-18
5.1 埋め込みアプリケーションで使用できるデー
タ定義文 . . . . . . . . . . . . . . . . . . . 5-1
6.1 InterBase でサポートするデータ型 . . . 6-2
6.2 SQL 式の構成要素 . . . . . . . . . . . . . 6-4
6.3 算術演算子. . . . . . . . . . . . . . . . . . 6-6
6.4 比較演算子. . . . . . . . . . . . . . . . . . 6-8
6.5 サブクエリーを必要とする InterBase の比
較演算子 . . . . . . . . . . . . . . . . . . . 6-8
6.6 演算子の種類別優先順位. . . . . . . . . . 6-15
6.7 算術演算子の優先順位 . . . . . . . . . . . 6-16
6.8 比較演算子の優先順位 . . . . . . . . . . . 6-16
6.9 論理演算子の優先順位 . . . . . . . . . . . 6-17
6.10 CAST() で変換できるデータ型 . . . . . . 6-17
6.11 SELECT 文の句 . . . . . . . . . . . . . . . 6-20
6.12
6.13
6.14
7.1
7.2
7.3
7.4
8.1
8.2
8.3
8.4
8.5
12.1
13.1
13.2
13.3
13.4
14.1
14.2
14.3
14.4
14.5
7
SQL の集計関数 . . . . . . . . . . . . . . 6-22
WHERE 句の検索条件の要素 . . . . . . . 6-28
ROWS 句の形式 . . . . . . . . . . . . . . 6-33
日付時刻情報の取り出し . . . . . . . . . . 7-2
SQL データ型から日付時刻データ型への
キャスト . . . . . . . . . . . . . . . . . . . 7-7
日付時刻データ型から他の SQL データ型へ
のキャスト . . . . . . . . . . . . . . . . . . 7-8
日付 / 時刻データ型の加算と減算 . . . . 7-10
InterBase で定義されている BLOB サブタ
イプ . . . . . . . . . . . . . . . . . . . . . . 8-2
BLOB に関する API 呼び出し. . . . . . 8-11
isc_blob_ctl のフィールドの説明. . . . 8-17
BLOB アクセス処理 . . . . . . . . . . . 8-19
BLOB フィルタの戻り値 . . . . . . . . . 8-20
SQLCODE の値. . . . . . . . . . . . . . . 12-1
XSQLDA のフィールド . . . . . . . . . 13-9
XSQLVAR のフィールド . . . . . . . . 13-10
SQL のデータ型、マクロ式、C のデータ型.
13-12
SQL 文の文字列と推奨される処理方法 13-17
すべてのプラットフォームで使用可能な
gpre 言語スイッチ . . . . . . . . . . . . . 14-2
追加の gpre 言語スイッチ . . . . . . . . 14-2
gpre オプション スイッチ . . . . . . . . 14-3
言語固有の gpre オプション スイッチ . 14-4
ファイル拡張子による言語指定 . . . . . 14-5
8
図
8.1
8.2
データベース中の BLOB ID と BLOB セグ
メントとの関連 . . . . . . . . . . . . . . . 8-4
小文字のテキストから大文字のテキストへの
変換 . . . . . . . . . . . . . . . . . . . . . . 8-13
大文字のテキストから小文字のテキストへの
変換. . . . . . . . . . . . . . . . . . . . . . 8-13
8.4 アプリケーション、InterBase、フィルタの
関係. . . . . . . . . . . . . . . . . . . . . . 8-15
13.1 XSQLDA と XSQLVAR の関係 . . . . . 13-8
8.3
9
10
第
章
埋め込み SQL ガイドの使い方
第1章
InterBase の『埋め込み SQL ガイド』では、InterBase とプログラミング言語(C または
C++)を使用する埋め込みデータベースアプリケーション(SQL と DSQL)のプログラム
記述、前処理、コンパイル、およびリンクの方法について、実際のタスクを中心に説明し
ます。この章では、このガイドの対象読者、および各章の概要について説明します。
対象読者
InterBase の『埋め込み SQL ガイド』は、データベースアプリケーションのプログラマを
対象とし、以下についての知識があることを前提としています。
• SQL 文
• リレーショナルデータベースのプログラミング
• C プログラミング
『埋め込み SQL ガイド』では、過去に InterBase を扱った経験は特に必要とされません。
InterBase の概要については『操作ガイド』を、SQL の概要については『言語リファレン
ス』を参照してください。
メモ 『埋め込み SQL ガイド』では、C または C++ 言語を使用した埋め込みプログラミング
(SQL と DSQL)について主に説明します。Delphi については扱っていません。
第 1 章 埋め込み SQL ガイドの使い方
1-1
このガイドの内容
このガイドの内容
下の表では、『埋め込み SQL ガイド』の各章について簡単に説明します。
表 1.1 『埋め込み SQL ガイド』の構成
章
説明
第 1 章「埋め込み SQL ガイドの使い方」
ガイドの構造と対象者についての説明
第 2 章「アプリケーションの必要条件」
SQL および DSQL アプリケーションのプログラミング
に共通する要素についての説明
第 3 章「データベースの操作」
データベースを扱う SQL 文の使い方についての説明
第 4 章「トランザクションの操作」
SQL 文によるトランザクションの使用とコントロールの
方法についての説明
第 5 章「データ定義文」
SQL データ定義文をアプリケーションに埋め込む方法に
ついての説明
第 6 章「データ処理」
アプリケーションで標準 SQL データの選択、挿入、更新、
および削除を行う方法についての説明
第 7 章「日付と時刻」
アプリケーションで DATE、TIME、および TIMESTAMP
データの選択、挿入、更新、および削除を行う方法につ
いての説明
第 8 章「BLOB データの操作」
アプリケーションで BLOB データの選択、挿入、更新、
および削除を行う方法についての説明
第 9 章「配列の使い方」
アプリケーションで配列データの選択、挿入、更新、お
よび削除を行う方法についての説明
第 10 章「ストアドプロシージャ」
アプリケーションでストアドプロシージャを呼び出す方
法についての説明
第 11 章「イベントの操作」
トリガーとアプリケーションの対話についての説明。ア
プリケーションでイベントの登録、イベントの待機、お
よびイベントへの応答を行う方法についての説明
第 12 章「エラー処理と回復」
アプリケーションにおける SQL 文のエラーのトラップ
と処理の方法
第 13 章「動的 SQL」
DSQL アプリケーションのコーディングについての説明
第 14 章「前処理、コンパイル、リンク」
ソースコードを実行可能なアプリケーションに変換する
方法についての説明
1-2 埋 め 込 み S Q L ガ イ ド
第
章
アプリケーションの必要条件
第2章
この章では、InterBase SQL と動的 SQL(DSQL)アプリケーションのプログラミングに
必要な条件について説明します。これらの条件の多くは、既存のアプリケーションを
InterBase に移動する開発者にも関係します。
すべてのアプリケーションの必要条件
埋め込みアプリケーションは、InterBase のプリプロセッサである gpre で処理されること
になります。したがって、アプリケーションにはいずれも、gpre による処理が正常に行わ
れるように、適切な宣言や文が置かれていなければなりません。また、SQL とホスト言語
(アプリケーションを記述する言語)との間で通信が可能になるような宣言や文も入れてお
く必要があります。このような処理をまとめると次のようになります。
• SQL とアプリケーション間のデータのやりとりで使用される変数の宣言
• プログラムでアクセス対象とするデータベースの宣言と設定
• プログラムで使用される非デフォルトトランザクション(名前付きトランザクション)の
ためのトランザクションハンドルの作成
• SQL(およびオプションで DSQL)文の記述
• エラーの対処と回復処理
• プログラム終了前に全トランザクションおよびデータベースを閉じる処理
なお、DSQL アプリケーションや、実行時における SQL 文の作成や、ユーザーによる SQL
文の作成が可能なタイプのアプリケーションでは、上記以外の処理が必要になります。
DSQL に必要な処理の詳細は、2-9 ページの「DSQL の必要条件」を参照してください。
gpre の使い方の詳細は、第 14 章「前処理、コンパイル、リンク」を参照してください。
第 2 章 アプリケーションの必要条件
2-1
すべてのアプリケーションの必要条件
SQL アプリケーションを移植する場合の注意
既存の SQL アプリケーションを InterBase へ移植するときは、次の点を理解しておくこと
も 必 要 で す。つ ま り、SQL アプリケーションの移植では、通常、BEGIN DECLARE
SECTION 文と END DECLARE SECTION 文の間で変数を宣言しておく必要がありま
す。これに対して、InterBase では、このような処理は必要ありません。移植元の SQL ア
プリケーションに置かれている DECLARE SECTION は、gpre によって自動的に処理さ
れるためです。また、セクション内で変数を全部宣言しておくと、移植の完成度が高くなり
ます。
DSQL アプリケーションを移植する場合の注意
InterBase には、既存の DSQL アプリケーションも移植することができます。この場合、
DSQL アプリケーションによっては、文中で他社の SQL デスクリプタエリア(SQLDA)
が使用されているものもあります。このような DSQL アプリケーションを InterBase に移
植する場合、その部分を InterBase で使用されている拡張 SQLDA(XSQLDA)に修正し
なければなりません。
ホスト変数の宣言
ここでいう変数とは、標準変数の 1 つで、データベースから読み出された値の保持に使用
されるものを言います。また、データベースへの書き出しのため値を変換したり、データ
ベースの検索条件を記述した値の保存にも使用されます。変数と SQL の関係、および動作
は次のとおりです。
• データの検索時、データベース項目の値が SQL により変数に移され、この時点で値の表示
や操作が可能になります。
• ユーザーがデータを入力した場合、そのデータはいったん変数に保持されます。この後、
SQL の INSERT または UPDATE 文により InterBase へ渡されます。
• SELECT 文での検索条件の指定では、条件を直接入力したり、変数の形で入力することが
できます。たとえば、次の 2 行は SELECT 文中の WHERE 句ですが、いずれも有効です。
上の WHERE 句は直接入力の例で、下の WHERE 句は変数 country を使った例です。こ
の場合、COUNTRY 列で比較が行われることになります。
… WHERE COUNTRY = 'Mexico';
… WHERE COUNTRY = :country;
なお、変数は、データベース中でアクセスされるデータ列全部について必ず 1 つずつ宣言
しておかなければなりません。また、変数は、他の標準変数と同じくグローバルに宣言して
おいたり、各種グローバル宣言がまとめられている DECLARE SECTION があれば、そこ
に記述しておいてもかまいません。SQL プログラムにおける、ホスト変数からの読み出し、
およびホスト変数への書き出しの詳細は、第 6 章「データ処理」を参照してください。
SQL プログラムでの変数の宣言方法は、標準言語変数とまったく同じです。つまり、宣言、
初期化、操作については、すべて標準言語規則に準拠します。たとえば、C 言語では、変数
を事前に宣言しておくことで、SQL 文で変数として使用できるようになります。
int empno; char fname[26], lname[26];
2-2 埋 め 込 み S Q L ガ イ ド
すべてのアプリケーションの必要条件
なお、他の SQL プログラムとの互換性を考え、BEGIN DECLARE SECTION 文と END
DECLARE SECTION 文の間で変数を宣言しておくこともできます。
セクションの宣言
SQL プログラムでは、BEGIN DECLARE SECTION 文と END DECLARE SECTION 文
の間で変数を宣言しておくのが一般的です。InterBase でも、移植性と互換性を考慮し、
DECLARE SECTION がサポートされています。この場合、構文は次のようになります。
EXEC SQL
BEGIN DECLARE SECTION;
<hostvar>;
. . .
EXEC SQL
END DECLARE SECTION;
次の例は、C 言語によるコードを使用して、1 つの DECLARE SECTION 中で empno、
fname、lname という 3 つの変数を宣言しています。
EXEC SQL
BEGIN DECLARE SECTION;
int empno;
char fname[26];
char lname[26];
EXEC SQL
END DECLARE SECTION;
このほか、変数のうち SQL 文で使用しないものは、DECLARE SECTION 文の外で宣言
してもかまいません。
BASED ON による変数の宣言
InterBase では、宣言句である BASED ON をサポートしています。この BASED ON を使
用して、データベース中の列定義をもとに C 言語の文字変数を作成することができます。
BASED ON の場合、作成した変数の容量が大きく、CHAR 列または VARCHAR 列に最
大数の文字を保持できるという特徴があります。また、ヌル終端文字のためのバイトも確
保されるので、C 言語の文字列関数を使用する際もほとんど問題はありません。
BASED ON は、次のように入力します。
BASED ON <dbcolumn> hostvar;
次の例は、従業員データベースの FIRSTNAME と LASTNAME という 2 つの列定義をも
とにして、fname、lname という 2 つの変数を宣言しています。
BASED ON EMP.FIRSTNAME fname;
BASED ON EMP.LASTNAME lname;
上記の文を C または C++ 言語のプログラムに埋め込んでおいた場合、前処理の際に次の
変数宣言が生成されます。
char fname[26];
char lname[26];
BASED ON は、次の手順で使用します。
第 2 章 アプリケーションの必要条件
2-3
すべてのアプリケーションの必要条件
1 SET DATABASE を使用して、取り出す列定義が含まれているデータベースを指定し
ます。
2 CONNECT を使用して、データベースにアクセスします。
3 BEGIN DECLARE SECTION でセクションを宣言します。
4 BASED ON 文を使用して、適切な型の文字列変数を宣言します。
以下は、上記の BASED ON 宣言をコンテキストに収めた例です。
EXEC SQL
SET DATABASE EMP = 'employee.ib';
EXEC SQL
CONNECT EMP;
EXEC SQL
BEGIN DECLARE SECTION;
int empno;
BASED ON EMP.FIRSTNAME fname;
BASED ON EMP.LASTNAME lname;
EXEC SQL
END DECLARE SECTION;
ホスト言語のデータ構造体
ホスト言語でデータ構造体がサポートされている場合、構造体の各データフィールドは
データベースの各列に対応することになります。次の例は、BILLING_ADDRESS という
構造体を作成する C 言語の宣言です。この宣言では、変数(データメンバー)は全部で 6
個あり、各変数はテーブル中の同じ名前の列に対応します。
Example 2.1ホスト言語のデータ構造体を使ってテーブル列にアクセスする
struct
{
char fname[25];
char lname[25];
char street[30];
char city[20];
char state[3];
char zip[11];
} billing_address;
SQL では、構造体の中のデータメンバーの認識は可能ですが、構造体からデータを読み
取ったり、データを構造体へ書き込んだりするには、SQL 文で個々のメンバーを指定し
ておく必要があります。次の例は、テーブルのデータを読み取って、C 言語の構造体
BILLING_ADDRESS の変数に入れるための SQL 文です。
Example 2.2SQL を使ってテーブルデータを C の構造体に読み込む
EXEC SQL
SELECT FNAME, LNAME, STREET, CITY, STATE, ZIP
INTO :billing_address.fname, :billing_address.lname,
:billing_address.street, :billing_address.city,
2-4 埋 め 込 み S Q L ガ イ ド
データベースの宣言と初期化
:billing_address.state, :billing_address.zip
FROM ADDRESSES WHERE CITY = 'Brighton';
データベースの宣言と初期化
1 つの SQL プログラムからでも、複数の InterBase のデータベースに同時にアクセスでき
ます。この場合、その SQL プログラム上で、アクセス対象のデータベースがすべて宣言さ
れていなければなりません。また、初期化も必要で、この宣言と初期化によって SQL トラ
ンザクションでデータベースへのアクセスが可能になります。一方、1 つのデータベースに
しかアクセスしないプログラムでは、gpre コマンドラインでデータベースを指定しておけ
ば、データベースの宣言は必要ありません。また、データベースハンドルを割り当てる必
要もありません。
重要
DSQL プログラムの場合は、複数のデータベースへのアクセスはできません。
データベース操作用の SQL 文としては、InterBase では次の 2 つをサポートしています。
• SET DATABASE 文:アクセス対象のデータベース名の宣言、データベースハンドルの割
り当てが可能です。
• CONNECT 文:ハンドルによって指定されたデータベースを開くことができます。また、
システムリソースの割り当ても実行されます。
CONNECT 文では、データベースの指定に名前ではなくデータベースハンドルを使用しま
す。このデータベースハンドルは、トランザクション内部のテーブル名の識別にも利用でき
ます。SQL プログラムのデータベースハンドルの詳細は、第 3 章「データベースの操作」
を参照してください。
SET DATABASE の使用
SET DATABASE 文では、次の処理が可能です。
• データベースハンドルの宣言。SQL プログラムで使用するデータベースごとに宣言を行い
ます。
• データベースハンドルに対する実際のデータベース名の関係付け。 データベースハンドル
名には、通常、実際のデータベース名の略語で、覚えやすいものを選択します。
SET DATABASE では、まず、データベースハンドルを指定します。このデータベースハ
ンドルが変数を示すことになります。したがって、変数を明示的に宣言する必要はありま
せん。この SET DATABASE によりデータベースハンドルにポインタが収められ、後続の
SQL 文では、このハンドルによってデータベースが参照されることになります。SET
DATABASE 文は、次のように入力します。
EXEC SQL
SET DATABASE handle = 'dbname';
アクセス対象となるデータベースが複数ある場合、各データベースについて
SET
DATABASE 文を記述します。たとえば、employee.ib と employee2.ib という 2 つのデータ
ベースがあり、ハンドルをそれぞれ DB1、DB2 と指定するときには次のように入力します。
第 2 章 アプリケーションの必要条件
2-5
データベースの宣言と初期化
EXEC SQL
SET DATABASE DB1 = 'employee.ib';
EXEC SQL
SET DATABASE DB2 = 'employee2.ib';
データベースハンドルの指定とデータベースの関係付けが終われば、以後、CONNECT な
ど後続の SQL のデータベースおよびトランザクション関連の文で、必要に応じて、この
データベースハンドルを使用することができます。
メモ
SET DATABASE では、 オプションでユーザー名とパスワードも指定できます。
SET DATABASE のオプションの詳細は、 第 3 章 「データベースの操作」 を参照してく
ださい。
CONNECT の使用
CONNECT 文は、データベースに接続する場合に使用します。CONNECT により、デー
タベースが開かれ、システムリソースが割り当てられます。データベースのテーブルは、
データベースが開かれてから使用可能になります。CONNECT は、次のようにプログラム
中に入力します。
EXEC SQL
CONNECT handle;
CONNECT 文は、アクセス対象となるデータベースが複数ある場合、各データベースにつ
いて個別に記述することも、1 つの CONNECT で複数のデータベースを指定することもで
きます。たとえば、データベースが 2 つで個別に指定する場合は次のように入力します。
EXEC SQL
CONNECT DB1;
EXEC SQL
CONNECT DB2;
また、1 つの CONNECT で一括して指定する場合は次のようになります。
EXEC SQL
CONNECT DB1, DB2;
CONNECT によりデータベースへの接続が完了すると、そのデータベースのテーブルは後
続のトランザクションで使用可能になります。データベースハンドルは、テーブル名の識別
にも使用できます。ただし、この識別は SQL アプリケーション上で有効で、DSQL アプリ
ケーションではできません。CONNECT のオプションとデータベースハンドルの扱いの詳
細は、第 3 章「データベースの操作」を参照してください。
データベースが 1 つの場合
プログラムでアクセスするデータベースが 1 つで、そのプログラムを gpre -m スイッチを
使用せずに前処理を行う場合、SET DATABASE と CONNECT は必ずしも記述する必要
はありません。これは、-m スイッチを使用しないときは、トランザクションが自動的に生
成されるためです。ただしこの場合でも、SET DATABASE と CONNECT はできるだけ
2-6 埋 め 込 み S Q L ガ イ ド
SQL 文
記述するのが理想的です。この 2 つの文を記述することで、コードの内容がわかりやすく
なります。SET DATABASE と CONNECT を省略するときは、次のような処理を行いま
す。
1
プログラムコード中、グローバル変数が定義されている箇所に、DECLARE
SECTION を挿入します。この場合、プログラムで変数が使用されていなければ、空の
DECLARE SECTION でもかまいません。空の DECLARE SECTION は次のようにな
ります。
EXEC SQL
BEGIN DECLARE SECTION;
EXEC SQL
END DECLARE SECTION;
2
プリコンパイル(前処理)時、gpre コマンドライン上でデータベース名を指定します。
ただし、プログラムに CREATE DATABASE 文が記述されているときは、データベー
スの指定の必要はありません。
データベースが 1 つの場合の扱いの詳細は、第 3 章「データベースの操作」を参照してく
ださい。
SQL 文
SQL アプリケーションとは、C や C++ などのホスト言語で書かれたプログラムで、その
中に SQL 文や DSQL 文が埋め込まれているものを指します。InterBase では、各種の SQL
文、DSQL 文をサポートしており、このような文はどのようなものでもホスト言語に埋め
込むことができます。埋め込みにあたっては、次の規則にしたがわなければなりません。
• 先頭にキーワードである EXEC SQL を入力します。
• ホスト言語がサポートしている文ターミネータ(文終端記号)を使って文を終了します。た
とえば、C と C++ では、ターミネータはセミコロン(;)です。
InterBase でサポートされている SQL 文と DSQL 文の詳細は、『言語リファレンス』を参
照してください。
エラー処理と復旧
SQL 文が実行されるたびに、エラーコードが SQLCODE 変数で返されます。SQLCODE
は gpre で前処理を行う際に自動的に宣言されるので、SQL プログラム上での宣言は必要
ありません。SQL の処理が終わった後、この SQLCODE をチェックすることで、実行時エ
ラーを調べたり、復旧することができます。
SQL には WHENEVER 文が用意されており、この文を使って SQLCODE の内容をチェッ
クしたり、プログラムの流れを回復処理へ持っていくことができます。このほか、SQL 文
の実行のたびに、SQLCODE を直接実行することもできます。SQL のエラー処理と復旧の
詳細は、第 12 章「エラー処理と回復」を参照してください。
第 2 章 アプリケーションの必要条件
2-7
トランザクションの終了
トランザクションの終了
トランザクションは、そのトランザクションでの処理が完了するたびに終了する必要があ
ります。同様に、途中でエラーが発生して処理が完了できなかったときも、終了しなけれ
ばなりません。トランザクションを終了せずにプログラムを終了した場合、limbo トランザ
クションが発生することがあります。limbo トランザクションでは、レコードはデータベー
スに入力されますが、そのレコードは保存されず、入力のロールバックもできません。な
お、limbo トランザクションは、InterBase に付属しているデータベース管理ツールで削除
することができます。
変更の保存
トランザクションは、COMMIT 文を使って終了します。トランザクションが終了すると
同時に、トランザクションで行った変更が保存され、その変更が他のユーザーにも有効に
なります。また、カーソルも閉じられます。COMMIT は、1 つのトランザクションの処理
をすべて無事に終了した時点で変更を保存するように実行します。COMMIT を使ってト
ランザクションを終了する場合、構文は次のようになります。
EXEC SQL
COMMIT TRANSACTION name;
たとえば、MYTRANS という名前のトランザクションをロールバックする場合、次のよう
に入力します。
EXEC SQL
COMMIT TRANSACTION MYTRANS;
SQL トランザクション制御の詳細は、第 4 章「トランザクションの操作」を参照してくだ
さい。
変更の取り消し
トランザクションで行った変更は、ROLLBACK 文を使って取り消すことができます。
ROLLBACK 文では、現在のトランザクションが終了すると同時に、カーソルも閉じられ
ます。したがって、ROLLBACK は、エラーが発生してトランザクションの処理が中断し
たときに使用します。ROLLBACK 文を使ってトランザクションを終了するには次のよう
に入力します。
EXEC SQL
ROLLBACK TRANSACTION name;
たとえば、MYTRANS という名前のトランザクションをコミットする場合、次のように入
力します。
EXEC SQL
ROLLBACK TRANSACTION MYTRANS;
なお、トランザクションに名前がない場合(つまり、デフォルトトランザクションの場合)、
ROLLBACK は次の文を使用します。
EXEC SQL
2-8 埋 め 込 み S Q L ガ イ ド
データベースを閉じる
ROLLBACK;
SQL トランザクション制御の詳細は、第 4 章「トランザクションの操作」を参照してくだ
さい。
データベースを閉じる
現在使用しているデータベースが必要なくなった場合は、そのデータベースを閉じてから
プログラムを終了する必要があります。データベースを閉じずにプログラムを終了してし
まうと、その後、そのデータベースを使用しようとした場合に、利用できなくなったり、
データベースが破損する可能性があります。データベースは、次の 2 とおりの方法で閉じ
ることができます。
• DISCONNECT 文を使用します。この文で、データベースへの接続が解除されるととも
に、ファイルも閉じられます。
• COMMIT または ROLLBACK で RELEASE オプションを使用します。
DISCONNECT、COMMIT RELEASE、および ROLLBACK RELEASE では、次の処理
が行われます。
• 開かれているデータベースファイルが閉じられます。
• リモートデータベースとの接続が解除されます。
• 使用中のデータベースに関する情報や InterBase エンジンによりコンパイルされたリクエ
ストがメモリ領域から解放されます。
メモ
SQL-92 標準との互換性を考慮した場合、データベースを閉じるには DISCONNECT を使
用するのが原則です。
データベースを閉じる方法の詳細は、第 3 章「データベースの操作」を参照してください。
DSQL の必要条件
これまで、SQL アプリケーションを作成する場合に必要になる処理や手続きについて説明
しましたが、このような作業は DSQL アプリケーションを作成する場合にもすべて当ては
まります。DSQL では、さらにいくつか処理が必要になります。これは、DSQL アプリケー
ションでは、実行時にユーザーがその場で SQL 文を入力することができるためです。この
種の SQL 文は数も多く、ユーザーが入力するさまざまな SQL 文に対応するため、DSQL
アプリケーションでは次のようなプログラミング作業が必要です。
• アプリケーション上で、拡張 SQL デスクリプタエリア(XSQLDA)をできるだけ多く宣
言しておきます。XSQLDA 構造体は、通常、少なくとも 2 つは宣言します。アプリケー
ションが複雑であれば、それ以上必要です。
• コンパイル時、プログラムで使用しているトランザクション名とデータベースハンドルを
残らず宣言します。トランザクション名とデータベースハンドルは、動的に扱われること
がないため、できるだけ多く宣言して実行時のユーザーの要求に応える必要があります。
• なんらかの処理を用意し、ユーザーからの SQL 文を受け取ります。
第 2 章 アプリケーションの必要条件
2-9
DSQL の必要条件
• 実際の処理に先立って、ユーザーから受け取った SQL 文を準備します。
PREPARE を使用して、文の情報を XSQLDA にロードできます。
• EXECUTE で準備された文を実際に処理します。
なお、EXECUTE IMMEDIATE 文を使用すると、1 つで PREPARE と EXECUTE の両方
の機能を同時に実行できます。EXECUTE IMMEDIATE 文については、
『言語リファレン
ス』を参照してください。
また、BLOB データの場合、他のデータ型とはカーソルの構文が異なります。BLOB カー
ソル文の詳細は、『言語リファレンス』を参照してください。
XSQLDA の宣言
拡張 SQL デスクリプタエリア(XSQLDA)は、いわば仲介領域で、この XSQLDA を介
してアプリケーションと InterBase のエンジンの間で情報が受け渡されることになります。
XSQLDA を使用することにより、次の 2 種類の処理が可能です。
• 入力パラメータをホスト言語のプログラムから SQL へ渡します。
• SELECT 文またはストアドプロシージャにより生成された出力を SQL からホスト言語の
プログラムに渡します。
ただし、1 つの XSQLDA では、上記の処理のうち一度にどちらか一方しか実行できませ
ん。このため、アプリケーションでは、入力用と出力用の XSQLDA を 1 つずつ宣言する
のが一般的です。
XSQLDA 構造体は InterBase のヘッダーファイル(ibase.h)で定義されています。この
ヘッダーファイルは、プログラムを gpre で前処理を行う際に自動的にインクルードされま
す。
重要
バージョン 3.3 より前の InterBase で記述された DSQL アプリケーションは、古い SQL デ
スクリプタエリアである SQLDA を使用しています。現在は、SQLDA および gpre -sqlda
スイッチはサポートされていません。このような古いアプリケーションは、XSQLDA を使
用するように変更する必要があります。
XSQLDA 構造体を作成するには、DECLARE SECTION で XSQLDA を正規のホスト言
語データ型として設定しておく必要があります。たとえば、次の例は、inxsqlda と outxsqlda
という 2 つの XSQLDA 構造体の設定です。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
XSQLDA inxsqlda;
XSQLDA outxsqlda;
. . .
EXEC SQL
END DECLARE SECTION;
. . .
2-10 埋 め 込 み S Q L ガ イ ド
DSQL に関する制限事項
このようにして XSQLDA の宣言を記述したアプリケーションを gpre で前処理した場合、
自動的にヘッダーファイル(ibase.h)がインクルードされ、XSQLDA がホスト言語のデー
タ型として定義されます。XSQLDA の構造の詳細は、第 13 章「動的 SQL」を参照してく
ださい。
DSQL に関する制限事項
プログラマは、DSQL を使用することにより、ユーザーの要求に幅広く対応できる柔軟な
アプリケーションの作成が可能になります。ただし、SQL 文のすべてを動的に扱うことが
できるとは限りません。たとえば、データベースハンドルとトランザクション名は、アプリ
ケーションをコーディングする際に指定しておかなければならず、実行時にユーザーが変
更したり指定することは不可能です。また、DSQL では、アプリケーションで複数のデー
タベースを扱えますし、複数のトランザクションの同時処理も可能ですが、次のような制
限があります。
• 同時に複数のデータベースにアクセスすることはできません。
• 複数のトランザクションの同時処理は、現在有効なデータベースだけで可能です。
• ユーザーが DSQL 文を使ってトランザクション名を指定することはできません。つまり、
トランザクション名は、アプリケーションのコーディングの段階で設定および操作する必
要があります。
データベースハンドルの使い方
データベースハンドルは必ず静的に扱われ、変更されることはありません。また、アプリ
ケーションをコーディングするときに宣言しておかなければなりません。したがって、事前
にユーザーの要求に見合った数のデータベースハンドルを宣言しておく必要があります。
これで、実行時に、ユーザーからデータベースの指定があった場合、SET DATABASE に
より宣言したデータベースハンドルが割り当てられることになります。この処理は、たと
えば、C 言語のコードでは次のようになります。
. . .
EXEC SQL
SET DATABASE DB1 = 'dummydb.ib';
EXEC SQL
SET DATABASE DB2 = 'dummydb.ib';
. . .
printf("Specify first database to open: ");
gets(fname1);
printf("¥nSpecify second database to open: ");
gets(fname2);
EXEC SQL
SET DATABASE DB1 = :fname1;
EXEC SQL
SET DATABASE DB2 = :fname2;
. . .
第 2 章 アプリケーションの必要条件
2-11
DSQL に関する制限事項
SET DATABASE の詳細は、第 3 章「データベースの操作」を参照してください。
有効なデータベースの使い方
DSQL アプリケーションでは、一度に 1 つのデータベースしか使用することができません。
これは、アプリケーションで複数のデータベースに接続するように記述されている場合も
同じです。また、DSQL 文はすべて、現在の有効なデータベースに対してだけ働きます。有
効なデータベースとは、SET DATABASE 文のデータベースハンドルにより関連付けられ
た最も新しいデータベースを指します。
一方、DSQL アプリケーション内の埋め込み SQL 文は、開かれているデータベース全部に
対して有効です。このため、ユーザーが入力した DSQL 文を介して、そのユーザーが指定
したデータベースに対して処理を実行させながら、その一方で非 DSQL 文(SQL 文)を
使ってユーザーの入力をログデータベースに記録することもできます。
SET DATABASE の詳細は、第 3 章「データベースの操作」を参照してください。
トランザクション名の使い方
SQL 文の中には、オプションでトランザクション名をパラメータとして付加できるものも
あります。この機能を使用して、たとえば、制御トランザクション(その処理部で使用さ
れるメインのトランザクション)を特定の文に対して指定することができます。トランザク
ション名は、DSQL アプリケーションでも使用できます。ただし、DSQL では、アプリ
ケーションのコンパイル時にトランザクション名を宣言しておく必要があります。これで、
そのトランザクション名がユーザー文に直接挿入されるようになります。ただし、そのた
めの処理は事前にプログラムに書き込んでおかなければなりません。
次は、宣言後、EXECUTE IMMEDIATE(EXECUTE も可)文にトランザクション名を
使用して、制御トランザクションを指定する場合の C 言語によるコード例です。
. . .
EXEC SQL
BEGIN DECLARE SECTION:
long first, second; /* トランザクション名を宣言する */
EXEC SQL
END DECLARE SECTION;
. . .
first = second = 0L; /* 名前を 0 に初期化します */
. . .
EXEC SQL
SET TRANSACTION first; /* トランザクション 1 を開始する */
EXEC SQL
SET TRANSACTION second; /* トランザクション 2 を開始します */
printf("¥nSQL> ");
gets(userstatement);
EXEC SQL
EXECUTE IMMEDIATE TRANSACTION first userstatement;
2-12 埋 め 込 み S Q L ガ イ ド
プログラムの前処理
. . .
トランザクションと名前の詳細は、第 4 章「トランザクションの操作」を参照してください。
プログラムの前処理
SQL プログラムまたは DSQL プログラムのコーディングができたら、InterBase のプリプ
ロセッサである gpre を使って前処理を実行しなければなりません。その後、プログラムは
コンパイルされ、リンクされます。gpre で前処理を実行すると、SQL 文と SQL 変数が、
ホスト言語のコンパイラで認識可能な文および変数に変換されます。gpre による前処理の
詳細は、第 14 章「前処理、コンパイル、リンク」を参照してください。
第 2 章 アプリケーションの必要条件
2-13
2-14 埋 め 込 み S Q L ガ イ ド
第
章
データベースの操作
第3章
ここでは、埋め込みアプリケーションでデータベース制御用の SQL 文をどのように使用す
るかについて説明します。次のような 3 つのデータベース操作関連の SQL 文を使用して、
データベースを設定し、開き、アクセスを可能にします。
• SET DATABASE では、データベースハンドルの宣言、およびデータベースハンドルと実
際のデータベースファイルの関連付けが可能です。また、データベースにパラメータを設
定し、各種の操作を行うこともできます。
• SET NAMES では、クライアントアプリケーションで使用しているキャラクタセットの指
定が可能です。サポートされているデータ型は、CHAR、VARCHAR、テキスト BLOB
の 3 種類です。クライアントアプリケーションで SET NAMES を使ってキャラクタセット
を指定しておくと、SELECT による処理の際、サーバー側でデータベースのデフォルト
キャラクタセットが指定されたキャラクタセットにコード変換されます。反対に、INSERT
や UPDATE での処理では、クライアントアプリケーションのキャラクタセットがデータ
ベースのデフォルトキャラクタセットにコード変換されます。
• CONNECT を使用して、データベースを開いて、システムリソースを割り当てることがで
きます。また、データベースにパラメータを設定し、各種の操作を行うこともできます。
データベースはいずれも、プログラムを終了する前に必ず閉じておかなければなりません。
DISCONNECT を使用して、または、プログラムの最後の COMMIT や ROLLBACK に
RELEASE オプションを追加して閉じることもできます。
データベースの宣言
データベースは、まず SET DATABASE で宣言しなければなりません。この宣言により、
データベースが開かれるとともにプログラムで使用可能になります。SET DATABASE で
は、次の作業を行います。
• データベースハンドルを指定します。
第 3 章 データベースの操作
3-1
データベースの宣言
• 指定したデータベースハンドルに、ローカルまたはリモートのノードに存在するデータ
ベースファイルを割り当てます。
データベースハンドルは、実際のデータベース名のエイリアスを略した、一意なものを使
用します。SET DATABASE で指定したデータベースハンドルは、以後の CONNECT、
COMMIT RELEASE、ROLLBACK RELEASE 文で使用され、各文は、データベースハ
ンドルで示されるデータベースに対して働くことになります。データベースハンドルは、ト
ランザクションブロック(処理部)の中でテーブル名の識別にも使用されます。たとえば、
2 つ以上のデータベースが開いており、各データベースの中に同じ名前のテーブルがあった
場合、データベースハンドルによりテーブルの識別、つまり特定が行われます。なお、DSQL
アプリケーションでは、データベースハンドルによる識別は行われません。
データベースハンドルは一意でなければならず、プログラムで使用されている変数と重複
してはなりません。また、データベースハンドルの名前としては、ホスト言語の予約語は使
用できず、InterBase の予約語も使用できません。
SET DATABASE 文によるデータベースの宣言は、基本的には次のようになります。
EXEC SQL
SET DATABASE DB1 = 'employee.ib';
上の文では、employee.ib というデータベースファイルをプログラムで使用するデータベー
スとして指定し、このデータベースに DB1 というデータベースハンドル、つまりエイリア
スを割り当てています。
なお、プログラムが動作するディレクトリとは異なるディレクトリにデータベースファイ
ルが置かれている場合は、SET DATABASE では、フルパス名を使ってファイルを指定し
なければなりません。たとえば、次の SET DATABASE 宣言では、フルパス名を使って
employee.ib を宣言しています。
EXEC SQL
SET DATABASE DB1 = '/InterBase/examples/employee.ib';
また、プログラムとデータベースファイルが異なるホストに置かれているときは、データ
ベースファイル名にはホスト名も加える必要があります。次の文は、TCP/IP ネットワーク
で、UNIX のホスト名を追加してデータベースファイルを指定している例です。
EXEC SQL
SET DATABASE DB1 = 'jupiter:/usr/interbase/examples/employee.ib';
NetBEUI プロトコルを使用した Windows ネットワークでは、パスが次のようになります。
EXEC SQL
SET DATABASE DB1 = '//venus/C:/InterBase/examples/employee.ib';
複数のデータベースの宣言
SQL プログラムでは、DSQL プログラムとは違って、複数のデータベースに同時に接続す
ることができます。このようなプログラムをマルチデータベースプログラムと言い、複数の
データベースハンドルも必要とされます。この場合、データベースハンドルには次の機能が
あります。
• マルチデータベーストランザクションでの個々のデータベースの参照
• テーブル名の識別
3-2 埋 め 込 み S Q L ガ イ ド
データベースの宣言
• CONNECT 文で開くように指示されたデータベースの特定
• DISCONNECT、COMMIT RELEASE、ROLLBACK RELEASE で閉じるように指示
されたデータベースの特定
一方、DSQL プログラムでは、一度に 1 つのデータベースにしかアクセスできません。こ
のため、データベースハンドルの機能は、単一のデータベースとの接続と接続解除に限定
されます。
マルチデータベースプログラムでは、データベースはそれぞれ個別に SET DATABASE 文
を使って宣言しておかなければなりません。たとえば、2 つの SET DATABASE 文を使っ
て 2 つのデータベースを宣言する場合、次のようになります。
. . .
EXEC SQL
SET DATABASE DB2 = 'employee2.ib';
EXEC SQL
SET DATABASE DB1 = 'employee.ib';
. . .
データベースハンドルの使用によるテーブル名の識別
アクセス対象のデータベースが複数ある場合、テーブル名が重複して使用されていること
もあります。こうしたデータベースに同時にアクセスするときは、データベースハンドル
を使ってテーブル名を識別することが必要です。この場合、handle.table のように、データ
ベースハンドルの名前を記述し、ピリオドを挟んで、テーブル名を指定します。
た と え ば、次 は、TEST と EMP という 2 つのデータベースハンドルを使用して、
EMPLOYEE という 2 つの同じ名前のテーブルを識別しているコード例です。
. . .
EXEC SQL
DECLARE IDMATCH CURSOR FOR
SELECT TESTNO INTO :matchid FROM TEST.EMPLOYEE
WHERE TESTNO > 100;
EXEC SQL
DECLARE EIDMATCH CURSOR FOR
SELECT EMPNO INTO :empid FROM EMP.EMPLOYEE
WHERE EMPNO = :matchid;
. . .
重要
データベースハンドルによるテーブル名の識別は、埋め込み SQL アプリケーションでのみ
可能です。DSQL アプリケーションでは、複数のデータベースへの同時アクセスはできな
いので、この機能は使用できません。
データベースハンドルを使った操作
マ ル チ デ ー タ ベ ー ス プ ロ グラムでは、SET DATABASE による宣言のほか、必ず
CONNECT 文でデータベースハンドルを指定しておかなければなりません。この指定によ
り、開く対象となるデータベースの識別が可能になります。また、以後のトランザクショ
ンで使用するための準備も行われます。
第 3 章 データベースの操作
3-3
データベースの宣言
また、DISCONNECT, COMMIT RELEASE, ROLLBACK RELEASE の各文では、デー
タベースハンドルを使用して、開かれているデータベースのサブセットを指定して閉じる
こともできます。
CONNECT を使ってデータベースを開いて操作するには、3-6 ページの「データベースを
開く」を参照してください。DISCONNECT、COMMIT RELEASE、または ROLLBACK
RELEASE を使ってデータベースを閉じるには、3-15 ページの「データベースを閉じる」
を参照してください。トランザクションにおけるデータベースハンドルの使い方の詳細は、
3-14 ページの「開かれているデータベースへのアクセス」を参照してください。
前処理時と実行時のデータベース
通常、SET DATABASE 文を使って 1 つのデータベースファイルを指定し、ハンドルと関
連付けます。この場合、プログラムを gpre で前処理すると、SET DATABASE で宣言した
データベースファイルの情報に基づいて、gpre によりプログラムのテーブルと列の参照先
を検査します。したがって、プログラムの実行時には、SET DATABASE で宣言したデー
タベースファイルと同じデータベースファイルにアクセスすることになります。必要であ
れば、前処理時と実行時で異なるデータベースを指定することができます。
COMPILETIME 句の使い方
場合によっては、同じ構造を持った複数のデータベースのどれにでもアクセスできるよう
に設計されたプログラムや、実行時に使用する予定のデータベースが前処理およびコンパ
イルの時点では使用できないことがあります。このような場合、SET DATABASE に
COMPILETIME 句を追加することで、gpre が前処理時にテストするデータベースを特定
することができます。たとえば、次は、SET DATABASE 文に COMPILETIME 句を付加
した例です。この場合、前処理時には、実際のデータベースの有無とはかかわりなく、
employee.ib というデータベースが gpre により形式的に使用されることになります。
EXEC SQL
SET DATABASE EMP = COMPILETIME 'employee.ib';
重要
COMPILETIME キーワードに続くファイル指定は、必ず引用符で囲んだハードコードさ
れた文字列にしなければなりません。
なお、SET DATABASE に RUNTIME 句ではなく COMPILETIME 句を追加し、また、
後続の CONNECT 文で SET DATABASE と同じデータベースを指定しているときは、
SET DATABASE で指定したデータベースと同じデータベースが前処理時および実行時に
使用されることになります。前処理時と実行時で異なるデータベースを使用することを
SET DATABASE で指定する場合は、COMPILETIME 句の他に RUNTIME 句を使用しま
す。
RUNTIME 句の使い方
前処理時にデータベースファイルを指定する場合、SET DATABASE は RUNTIME キー
ワードおよびランタイムファイル名を使用して、COMPILETIME で指定したデータベー
スとは異なるデータベースを実行時専用として指定することもできます。
EXEC SQL
SET DATABASE EMP = COMPILETIME 'employee.ib'
RUNTIME 'employee2.ib';
3-4 埋 め 込 み S Q L ガ イ ド
データベースの宣言
RUNTIME キーワードの右のファイル指定は、引用符で囲んでハードコードされた文字列
でも変数でも問題ありません。たとえば、次は、ユーザーにデータベース名の入力を要求
し、入力されたデータベース名を変数に入れる C 言語によるコード例です。この変数は、
次の SET DATABASE で使用されます。
. . .
char db_name[125];
. . .
printf("Enter the desired database name, including node
and path):¥n");
gets(db_name);
EXEC SQL
SET DATABASE EMP = COMPILETIME 'employee.ib' RUNTIME :db_name;
. . .
メモ
SET DATABASE の変数は、必ず先頭にコロンを付けなければなりません。
SET DATABASE のデータベースハンドルのスコープ
SET DATABASE で設定されたデータベースハンドルは、デフォルトでは、アプリケーショ
ンの全モジュールに対してグローバルに働きます。これを、グローバルハンドルと呼んでい
ます。このグローバルハンドルは、プログラムを構成している全部のホスト言語モジュー
ルで参照されることになります。SET DATABASE にはオプションで 2 つのキーワードが
ありますが、これらによってスコープを調整することができます。
• STATIC を挿入した場合、データベースハンドルのスコープは SET DATABASE 文が置
かれているモジュールに限定されます。したがって、STATIC を宣言していないモジュー
ルでは、データベースハンドルは無効で使用できません。
• SET DATABASE 文に EXTERN を挿入した場合、gpre による前処理時には、その SET
DATABASE 文が置かれているモジュールの他に、通常の SET DATABASE(EXTERN
なし)を記述したモジュールがあると解釈されます。このため、いずれかのモジュールで
EXTERN キーワードを使ったときは、別のモジュールに通常の SET DATABASE 文を必
ず記述しておく必要があります。通常の SET DATABASE の記述がないときは、コンパ
イル時にエラーが発生します。
上記のように、マルチモジュールプログラム(複数のモジュールで構成されるプログラム)
では、いずれかのモジュールだけに STATIC を使った SET DATABASE を記述すること
で、そのモジュールにデータベースハンドルのアクセスを限定することができます。
STATIC を使用する場合の SET DATABASE の記述は次のとおりです。
EXEC SQL
SET DATABASE EMP = STATIC 'employee.ib';
また、マルチモジュールプログラムで、いずれかのモジュールに EXTERN キーワードを
使った SET DATABASE を記述した場合、その SET DATABASE は実際の宣言ではなく、
別のモジュールに実際の SET DATABASE があることを示すことになります。gpre によ
る前処理時には、この情報を基に処理が行われます。EXTERN キーワードを使用する場合
の SET DATABASE の記述は、次のようになります。
EXEC SQL
第 3 章 データベースの操作
3-5
接続キャラクタセットの指定
SET DATABASE EMP = EXTERN 'employee.ib';
マルチモジュールプログラムで EXTERN を使用する場合、EXTERN の参照先となってい
る実際の SET DATABASE 宣言がまず行われるようにしなければなりません。また、宣言
したデータベースに対して、他のモジュールよりも先に、実際の SET DATABASE を置い
たモジュールからアクセスするように記述します。
1 つの SET DATABASE 文には、STATIC または EXTERN キーワードのどちらか一方し
か 指 定 で き ま せ ん。 SET DATABASE のスコープ宣言は、COMPILETIME または
RUNTIME が指定されたデータベースにも適用されます。
接続キャラクタセットの指定
クライアントアプリケーションがデータベースに接続する場合、固有のキャラクタセット
の要件を持っている場合があります。クライアントにデータベースアクセスを提供する
サーバーには、クライアントが指定しない限り、これらの要件はわかりません。クライアン
トアプリケーションは、データベースに接続する前に SET NAMES 文で、そのキャラクタ
セットの要件を指定します。
SET NAMES 文では、データをデータベースからクライアントアプリケーションへ変換す
るときにサーバーが使用するキャラクタセットを指定します。同様に、クライアントがデー
タをデータベースに送るときは、サーバーは、そのデータをクライアントのキャラクタセッ
トからデータベースのデフォルトキャラクタセット(各列に定義したキャラクタセットが
データベースのデフォルトキャラクタセットとは異なる場合には、そのキャラクタセット)
に変換します。
たとえば、次の文は、クライアントで使用しているキャラクタセットが SJIS で、そのクラ
イアントからデータベースに接続する例です。
EXEC SQL
SET NAMES DOS437;
EXEC SQL
CONNECT 'europe.ib' USER 'JAMES' PASSWORD 'U4EEAH';
キャラクタセットの詳細は、
『データ定義ガイド』を参照してください。SET NAMES と
CONNECT の構文の詳細は、『言語リファレンス』を参照してください。
データベースを開く
データベースは、宣言の後、CONNECT 文で接続しなければなりません。これで、データ
ベースを使用できるようになります。CONNECT では、次の処理が行われます。
• データベースの使用に必要になるシステムリソースの割り当て
• データベースファイルがローカルとリモートのどちらであるかの判定。ローカルデータ
ベースとは、アプリケーションを実行しているホストに置かれているデータベースを指し
ます。リモートデータベースとは、別のホストに置かれているデータベースを指します。
• データベースを開いて、データベースが正常かどうかの確認
3-6 埋 め 込 み S Q L ガ イ ド
データベースを開く
InterBase では、ローカルであれリモートであれ、データベースへのアクセスは透過的に行
われます。また、アクセスの際、データベース構造が異常だったり、オンディスク構造体
(ODS)番号が InterBase の必要とする番号と異なっていたり、データベースが破損してい
る場合、エラーが報告されるとともにアクセスが中止されます。
CONNECT ではオプションで次の設定を行うこともできます。
• ユーザー名とパスワードの設定。CONNECT でユーザー名とパスワードを指定しておく
と、サーバーのセキュリティデータベースとの照合が行われ、照合にパスすると接続が実
行されます。ユーザー名の長さは 31 文字以内となっています。パスワードは、8 文字以内
に制限されています。
• データベースへの接続時にユーザーが使用する SQL ロール名。これは、ユーザーに事前に
このロールのメンバーシップを付与されている場合に限ります。メンバーシップが付与さ
れているかどうかにかかわらず、ユーザーは ROLE 句で明示的に指定しない限り、どの
ロールにも属しません。クライアントは一度の接続において 1 つだけロールを指定でき、
再接続しない限り別のロールに切り替えることはできません。
• アプリケーションに割り当てられるデータベースキャッシュバッファのサイズの設定。デ
フォルトのキャッシュサイズが不足したときに設定します。
CONNECT 文の基本形
CONNECT 文では、開くデータベースの名前を指定するデータベースパラメータが 1 つ以
上必要です。データベースの名前は、次のいずれかを使って指定できます。
• 事前に SET DATABASE 文を使って宣言したデータベースハンドル
• ホスト言語変数
• 引用符で囲んだハードコードされたファイル名
データベースハンドルの使い方
SET DATABASE でデータベースハンドルを宣言している場合、後続の CONNECT 文で
は、引用符で囲んだハードコードされたデータベース名ではなく、できるだけデータベー
スハンドルを使ってデータベースを指定するようにします。次に例を示します。
. . .
EXEC SQL
SET DATABASE DB1 = 'employee.ib';
EXEC SQL
SET DATABASE DB2 = 'employee2.ib';
EXEC SQL
CONNECT DB1;
EXEC SQL
CONNECT DB2;
. . .
CONNECT でデータベースハンドルの使用を推奨するのは、次のような利点があるためで
す。
• 長いファイル名でなく、簡単で覚えやすい略語でデータベースの指定が可能
第 3 章 データベースの操作
3-7
データベースを開く
• ハンドルは、マルチデータベーストランザクションでテーブル名の識別に使用することが
可能。DSQL アプリケーションでは、マルチデータベーストランザクションのサポートを
行っていません。
• 必要に応じてハンドルを他のデータベースへ割り当て直すことが可能
• CONNECT パラメータを使ってデータベースキャッシュバッファの設定が可能
データベースキャッシュバッファの個数の設定の詳細は、3-13 ページの「データベース
キャッシュバッファの設定」を参照してください。
文字列またはホスト言語変数の使用
CONNECT では、データベースハンドルのほか、実行時に用意されるデータベース名を
使ってデータベースを指定することができます。実行時に用意されるデータベース名とし
ては、変数と、引用符で囲んだハードコードされたファイル名があります。
次は、アクセス対象のデータベースが 1 つのプログラムで、実行時にユーザーが指定した
ファイル名をもとに、変数を介して CONNECT を実行させる場合の C 言語のコード例で
す。
. . .
char fname[125];
. . .
printf('Enter the desired database name, including node
and path):¥n');
gets(fname);
. . .
EXEC SQL
CONNECT :fname;
. . .
ヒント
上記の処理は、同じ構造のデータベースがいくつもある場合、その中から必要なデータベー
スを選択できるようなプログラムを設計する際に特に有効です。同じ構造のデータベース
としては、CAD や CAM、その他の建築設計関連のデータベースが例として挙げられます。
複数のデータベースの処理例
上の例は、アクセス対象のデータベースが 1 つのプログラムの場合ですが、マルチデータ
ベースプログラムでも同様に、ユーザーの入力をもとに変数を使って CONNECT を実行す
ることができます。手順は、次のようになります。
1
次の SET DATABASE 構文を使ってデータベースハンドルを宣言します。
EXEC SQL
SET DATABASE handle = COMPILETIME 'dbname';
上の構文で、handle はデータベースハンドルで、プログラマが書き込みます。dbname
は引用符で囲まれてハードコードされたデータベース名で、前処理時に gpre によって
使用されます。
2
3
ユーザーに、開くデータベース名の入力を求めます。
ユーザーが入力したデータベース名を変数に保存します。
3-8 埋 め 込 み S Q L ガ イ ド
データベースを開く
4
データベースハンドルを使ってデータベースを開きます。次の CONNECT 構文で変数
とデータベースハンドルを関連付けます。
EXEC SQL
CONNECT :variable AS handle;
以上の処理は、C 言語のコードでは次のようになります。
. . .
char fname[125];
. . .
EXEC SQL
SET DATABASE DB1 = 'employee.ib';
printf("Enter the desired database name, including node
and path):¥n");
gets(fname);
EXEC SQL
CONNECT :fname AS DB1;
. . .
上記の SET DATABASE では、ハードコードされて引用符で囲まれたデータベースファイ
ル名がデータベースの指定に使用されています。このファイル名は、gpre による前処理で
必要になります。一方、プログラムの実行時には、変数の fname がデータベースの指定に
使用されることになります。
ハードコードされたデータベース名
1 つのデータベースにアクセスするプログラム
アクセス対象のデータベースが 1 つのプログラムでは、SET DATABASE によるデータ
ベースの宣言を省略できます。この場合、CONNECT では、必ず引用符で囲んでハード
コードされたファイル名を記述しなければなりません。形式は、次のようになります。
EXEC SQL
CONNECT '[host[path]]filename';
上記の host はホスト名ですが、このホスト名はアクセスするデータベースファイルとプロ
グラムが別のノードに置かれている場合に限り必要です。また、path はパスを表しますが、
このパスもプログラムとデータベースファイルの現在の作業ディレクトリが異なる場合に
の み 必 要 で す。 た と え ば、次の例は、UNIX ホスト名とパス名を含むファイル名の
CONNECT 文です。
EXEC SQL
CONNECT 'valdez:usr/interbase/examples/employee.ib';
メモ
データベースファイルを指定する場合のホスト構文は、サーバープラットフォームによっ
て異なります。
重要
複数のデータベースをアクセスするプログラムでは、上記のフォームの CONNECT は使用
できません。
第 3 章 データベースの操作
3-9
データベースを開く
マルチデータベースプログラム
複数のデータベースにアクセスするプログラムでは、個別の SET DATABASE 文を使用し
て、各データベースに対応するデータベースハンドルを宣言しなければなりません。ここで
宣言したデータベースハンドルは、後続の CONNECT 文で開くデータベースの特定に使用
されます。
. . .
EXEC SQL
SET DATABASE DB1 = 'employee.ib';
EXEC SQL
SET DATABASE DB2 = 'employee2.ib';
EXEC SQL
CONNECT DB1;
EXEC SQL
CONNECT DB2;
. . .
データベースを閉じた場合、そのデータベースハンドルは、以後使用されることはありま
せん。したがって、同じデータベースハンドルを別のデータベースに割り当てることができ
ます。割り当ては、データベースを閉じた後、CONNECT 文を使って行います。データ
ベースはハードコードされたファイル名で指定します。次に例を示します。
. . .
EXEC SQL
DISCONNECT DB1, DB2;
EXEC SQL
CONNECT 'project.ib' AS DB1;
. . .
3-10 埋 め 込 み S Q L ガ イ ド
データベースを開く
その他の CONNECT 構文
CONNECT にはさまざまな形式がサポートされ、プログラミングに柔軟性を与えていま
す。次に、これらの構文を表にして紹介します。表では、構文と処理内容、例を示してあり
ます。また、シングルデータベースプログラム(単一のデータベースにアクセスするプロ
グラム)とマルチデータベースプログラム(複数のデータベースにアクセスするプログラ
ム)で、CONNECT が使用可能かどうかについても示してあります。
表 3.1
CONNECT の構文のまとめ
シングル
アクセス
複数
アクセス
可
不可
可
可
‘dbfile’ 引用符で囲まれ、ハードコードされたデータベース 可
ファイル dbfile を開きます。また、handle は、すで
可
構文
説明
CONNECT ‘dbfile’;
引用符で囲まれ、ハードコードされた 1 つのデータ
ベースファイル dbfile を開きます。
例:
EXEC SQL
CONNECT ‘employee.ib’;
CONNECT handle;
handle は、すでに宣言されているデータベースハン
ドルで、関連付けられているデータベースファイル
を開きます。CONNECT 構文では、これが推奨され
ています。
例:
EXEC SQL
CONNECT EMP;
CONNECT
AS handle;
に宣言されているデータベースハンドルで、データ
ベースに割り当てられます。
例:
EXEC SQL
CONNECT ‘employee.ib’ AS EMP;
CONNECT
:varname AS handle;
varname は、変数で、この変数で示されるデータベー
スファイルを開きます。また、handle は、すでに宣
言されているデータベースハンドルで、このデータ
ベースハンドルがデータベースに割り当てられま
す。
可
可
例:
EXEC SQL
CONNECT :fname AS EMP;
CONNECT 構文と使い方の詳細は、『言語リファレンス』を参照してください。
複数のデータベースへの接続
CONNECT では、複数のデータベースを一括して開くこともできます。SET DATABASE
で宣言済みのデータベースをすべて開く場合は、次の CONNECT 構文のいずれかを使用し
ます。
第 3 章 データベースの操作
3-11
データベースを開く
EXEC SQL
CONNECT ALL;
EXEC SQL
CONNECT DEFAULT;
上記の他に、宣言済みの複数のデータベースの中から、データベースを指定して開くこと
もできます。この場合、開きたいデータベースをカンマで区切って記述します。たとえば、
ハンドルを使って 2 つのデータベースを開きたいときは、次の文を使用します。
EXEC SQL
CONNECT DB1, DB2;
また、次は、2 つのデータベースをハードコードのファイル名を使って開くと同時に、各
データベースに宣言済みのデータベースハンドルを割り当てる例です。
EXEC SQL
CONNECT 'employee.ib' AS DB1, 'employee2.ib' AS DB2;
ヒント
複数のデータベースプログラムでは、そのプログラムのデータベースへのアクセス方法が
簡潔で明快な場合は、CONNECT で複数のデータベースを開く方法がたいへん効率が高く
なります。ただし、複数のデータベースの開閉を繰り返したり、データベース名を変数に置
き換えたり、また、1 つのデータベースに複数のデータベースハンドルを割り当てるような
複雑なプログラムでは、この手法の使用は控えるのが賢明です。そのようなプログラムで
は、処理に応じて CONNECT 文を個別に記述した方がコードが読みやすく、デバッグや修
正も簡単です。
接続中のエラー処理
データベースの接続中に実行時エラーが発生することもありますが、このようなエラーは
WHENEVER 文を使ってトラップし、処理します。次の例は、C 言語のコードによるエラー
処理ルーチンです。このルーチンでは、エラーメッセージが表示されるとともに、プログ
ラムが正常に終了します。
. . .
EXEC SQL
WHENEVER SQLERROR
GOTO error_exit;
. . .
:error_exit
isc_print_sqlerr(sqlcode, status_vector);
EXEC SQL
DISCONNECT ALL;
exit(1);
. . .
SOL エラーの処理の詳細は、第 12 章「エラー処理と回復」を参照してください。
3-12 埋 め 込 み S Q L ガ イ ド
データベースを開く
データベースキャッシュバッファの設定
CONNECT では、データベースを開く他に、接続したデータベースのキャッシュバッファ
の値を指定することもできます。データベースへの接続が実行されるときは、InterBase に
よってシステムメモリ上にプライベートバッファとして使用できる領域が割り当てられま
す。このバッファがデータベースキャッシュバッファで、ここにアクセス対象のデータベー
スのページが記憶され、処理速度の向上が図れます。したがって、このデータベースキャッ
シュバッファが大きくなれば、プログラムから同時にアクセスできるデータベースのペー
ジ数も多くなることになります。データベースキャッシュバッファは、そのデータベースに
対するプログラムからのアクセスの終了後に解放されます。
データベースに割り当てられるデータベースキャッシュバッファの値は、デフォルトでは
256 です。このデフォルト値は、特定のデータベースまたはサーバー全体で変更できます。
• データベースのデフォルトキャッシュバッファのサイズを再設定するには、gfix ユーティ
リティを使用します。gfix を使用したデータベースのバッファサイズの設定の詳細は、
『操
作ガイド』を参照してください。
• サーバーレベルでデフォルトキャッシュバッファのサイズを変更するには、InterBase の環
境設定ファイルにある DATABASE_CACHE_PAGES の値を変更します。このオプショ
ンは、メモリを使いすぎたり、キャッシュを小さくしすぎることがあるため、慎重に使用
してください。
データベースごとのバッファの設定
一方、実際にアクセスするデータベースの数が多く、データベース中の行に対するアクセ
スや変更が多いプログラムでは、データベースキャッシュバッファの値を大きくすること
で処理速度を向上できる場合があります。なお、バッファの上限は、システムによって異な
ります。
• CACHE n パラメータと CONNECT を使用して、接続が持続している期間に、データベー
スに割り当てられるバッファの値を設定できます。n は、割り当てられるバッファの値で
す。データベースのバッファ値を設定するには、データベースの名前の右に CACHE n を
記述します。たとえば、次の CONNECT では、EMP というハンドルで参照されるデータ
ベースに対して、500 のバッファを設定しています。
EXEC SQL
CONNECT EMP CACHE 500;
メモ
データベースで現在使用されている最小のバッファを下回るバッファサイズを指定した場
合、この要求は無視されます。
また、次の文は、TEST と EMP という 2 つのデータベースを開いています。TEST には
CACHE が指定されていないため、バッファはデフォルトの 256 のままです。一方、EMP
は CACHE 句によりバッファ値 400 で開かれます。
EXEC SQL
CONNECT TEST, EMP CACHE 400;
第 3 章 データベースの操作
3-13
開かれているデータベースへのアクセス
全データベースに対するバッファの一括設定
上記の他に、CONNECT ALL と CACHE n パラメータを使用することにより、すべての
データベースに対して同じ値のバッファを一括して設定できます。たとえば、次の文は、
EMP と EMP2 という 2 つのデータベースを開くとともに、どちらのデータベースにも
400 のバッファを割り当てます。
. . .
EXEC SQL
SET DATABASE EMP = 'employee.ib';
EXEC SQL
SET DATABASE EMP2 = 'test.ib';
EXEC SQL
CONNECT ALL CACHE 400;
. . .
また、次の例のように、データベースそれぞれについて CACHE を使って同じバッファ値
を設定することもできます。
. . .
EXEC SQL
CONNECT EMP CACHE 400, TEST CACHE 400;
. . .
開かれているデータベースへのアクセス
データベースが開かれる(接続される)と、データベース中のテーブルにアクセスが可能
になります。この場合、データベースとトランザクションの関係には、次の 4 とおりあり
ます。
• アクセスするデータベースが 1 つで、実行するトランザクションが 1 つ
• アクセスするデータベースが 1 つで、実行するトランザクションが複数
• アクセスするデータベースが複数で、実行するトランザクションが 1 つ
• アクセスするデータベースが複数で、実行するトランザクションが複数
トランザクションの概要については、第 4 章「トランザクションの操作」を参照してくだ
さい。
テーブル名の識別
SQL では、複数のデータベースにアクセスしながら複数のトランザクションを実行する場
合、特に注意が必要です。たとえば、複数のデータベースにそれぞれ同じ名前のテーブルが
格納されていることがあります。この場合、トランザクションの実行の際は、テーブル名
の前にデータベースハンドルを付けてテーブル名を識別しなければなりません。
テーブル名を識別する場合、次のように、ピリオドを挟んでデータベース名とテーブル名
を記述します。
3-14 埋 め 込 み S Q L ガ イ ド
データベースを閉じる
handle.table
たとえば、次の例は、TEST と EMP という 2 つのデータベースがあり、どちらにも
EMPLOYEE という同じ名前のテーブルが格納されている場合のカーソルの宣言です。こ
こでは、テーブル名である EMPLOYEE の前に TEST と EMP を付けて、参照先のテーブ
ルを識別しています。
. . .
EXEC SQL
DECLARE IDMATCH CURSOR FOR
SELECT TESTNO INTO :matchid FROM TEST.EMPLOYEE
WHERE (SELECT EMPNO FROM EMP.EMPLOYEE WHERE EMPNO = TESTNO);
. . .
メモ
DSQL では、1 つの文で複数のデータベースにアクセスすることはできません。
データベースを閉じる
データベースの使用が終わったら、そのデータベースを閉じなければ(接続解除しなけれ
ば)なりません。SQL では、次の方法でデータベースを閉じることができます。
• DISCONNECT を使用して、データベースを接続解除してファイルを閉じます。
• COMMIT または ROLLBACK に RELEASE オプションを追加して、データベースから
接続解除してファイルを閉じます。
DISCONNECT、COMMIT RELEASE、および ROLLBACK RELEASE では、次の処理
が行われます。
• 開かれているデータベースファイルが閉じられます。
• リモートデータベースに接続している場合は、その接続解除を行います。
• データベースのメタデータ(データベースのシステムテーブル)情報と InterBase エンジン
によってコンパイルされたリクエストを保持しているメモリを解放します。
メモ
SQL-92 標準との互換性を考慮した場合、データベースは DISCONNECT で閉じるのが理
想的です。また、データベースは必要でなくなった時点で閉じるようにします。いったん閉
じたデータベースは、再度開いてリソースが割り当てられた時点で、また使用できるよう
になります。
DISCONNECT でデータベースを閉じる
開いた状態のデータベースをすべて接続を接続解除して閉じる場合、構文は次のようにな
ります。
EXEC SQL
DISCONNECT {ALL | DEFAULT};
つまり、次のどの文でも、開いた状態のデータベースをすべて閉じることができます。
EXEC SQL
第 3 章 データベースの操作
3-15
データベースを閉じる
DISCONNECT ALL;
EXEC SQL
DISCONNECT DEFAULT;
また、データベースハンドルをカンマで区切って指定することで、複数のデータベースを
閉じることができます。構文は次のとおりです。
EXEC SQL
DISCONNECT handle [, handle ...];
たとえば、次の文では、ハンドルが DB1 と DB2 の 2 つのデータベースを閉じます。
EXEC SQL
DISCONNECT DB1, DB2;
メモ
データベースは、そのデータベースに関連しているトランザクションがすべて終了してか
ら閉じなければなりません。なお、トランザクションの終了の前に閉じた場合、再度開い
てリソースを再割り当てする必要があります。
COMMIT と ROLLBACK によりデータベースを閉じる
開かれているデータベースを全部一括して閉じる場合、COMMIT または ROLLBACK も
使用できます。構文は次のとおりです。
EXEC SQL
{COMMIT | ROLLBACK} RELEASE;
たとえば、次は、開いた状態のデータベースを COMMIT を使って全部閉じる場合です。
EXEC SQL
COMMIT RELEASE;
このほか、COMMIT または ROLLBACK では、データベースを指定して閉じることもで
きます。この場合、RELEASE に続けてパラメータとしてデータベースハンドルを指定し
ます。構文は次のとおりです。
EXEC SQL
COMMIT | ROLLBACK RELEASE handle [, handle ...];
次の例は、ROLLBACK 文で 2 つのデータベースを閉じる場合です。
EXEC SQL
ROLLBACK RELEASE DB1, DB2;
3-16 埋 め 込 み S Q L ガ イ ド
第
章
トランザクションの操作
第4章
SQL には、データ定義およびデータ操作関連の文があり、トランザクションのコンテキス
トと関連して働きます。トランザクションとは、データと SQL 文の仲介役であり、プログ
ラムでは、このトランザクションと関連 SQL 文を使ってなんらかのタスクを実行すること
になります。この章では、SQL のトランザクション操作文を使用するトランザクションの
制御、および起動と終了の方法について説明します。
表 4.1
トランザクション管理の SQL 文
文
用途
SET TRANSACTION
トランザクションの起動、名前の割り当てが可能です。また、SET
TRANSACTION では、パラメータを使ってトランザクションの動作の
指定が可能です。パラメータのタイプは、次のとおりです。
アクセスモード:書き込みや読み取りなど、トランザクションで可能
になる動作を指定できます。
ロック対応:ロックの競合が起こったときの対応方法を指定できます。
排他レベル:トランザクションに与えられるデータベースのビューの
設定に使用します。
別のトランザクションから同一のデータベースに対
してなんらかの処理が発生した場合の対応方法を決めておくことがで
きます。
テーブル予約:通常、テーブルへのアクセスは、読み取り、書き込み
時にそのつど行われますが、
トランザクションの起動時に事前にアクセ
スするテーブルをロックしておくことができます。
データベース指定:開いた状態のデータベースのいくつかを指定し、そ
のデータベースにトランザクションの処理を限定することができます。
COMMIT
トランザクションでの変更をデータベースに保存する場合に使用しま
す。保存と同時に、トランザクションが終了します。
ROLLBACK
トランザクションでの変更の取り消しが可能です。それまでの変更は
データベースにコミットされずに、トランザクションが終了します。
第 4 章 トランザクションの操作
4-1
デフォルトトランザクションの起動
上記のトランザクション操作文でトランザクションの性質を定義することができます。ま
た、同一のデータに対して、別のトランザクションからアクセスがあったときの動作や対
応を指定しておくこともできます。この場合、別のトランザクションは、他のアプリケー
ションのものであってもかまいません。
InterBase では、次の 2 種類のトランザクションを使用できます。
• 1 つはデフォルトトランザクションで、事前に GDS__TRANS という名前が付けられてい
ます。プログラムで、SET TRANSACTION 文がないのになんらかのトランザクションを
必要とする文があった場合、このトランザクションが自動的に使用されます。アクセス
モードやロック対応など、GDS__TRANS の動作内容は事前に決められています。ただし、
このデフォルト設定は、SET TRANSACTION でパラメータを付加して変更することがで
きます。GDS__TRANS は、isc_tr_handle 型のグローバル変数として扱います。
メモ
SET TRANSACTION を記述せず、デフォルトトランザクションを使用する場合、gpre m スイッチを付けないで前処理を実行しなければなりません。
• もう 1 つは名前付きトランザクションで、SET TRANSACTION 文によって起動されま
す。名前はトランザクションごとに異なり、通常は SET TRANSACTION でパラメータを
使ってトランザクションの動作を指定します。
複数トランザクションを使用する場合と命名規則を除いて、デフォルトトランザクション
も名前付きトランザクションも同じようにトランザクションを制御できます。どの場合で
も、SET TRANSACTION にパラメータを付加することでアクセス、ロック対応、および
排他レベルを指定できます。
gpre の詳細は、第 14 章「前処理、コンパイル、リンク」を参照してください。トランザ
クションの動作の詳細は、4-7 ページの「SET TRANSACTION での動作の指定」を参照
してください。
デフォルトトランザクションの起動
動作を指定せずにトランザクションを起動した場合、次のデフォルトの動作が使用されます。
READ WRITE WAIT ISOLATION LEVEL SNAPSHOT
使用するトランザクションが 1 つのプログラムでは、このデフォルトトランザクションを
利用するのが有効です。これは、SET TRANSACTION の記述がなくても、必要な場合に
自動的にデフォルトトランザクションが起動するためです。デフォルトトランザクション
は、プログラム中に SET TRANSACTION を記述して明示的に起動することもできます。
SET TRANSACTION なしの起動
トランザクションが 1 つの簡単なプログラムでは、SET TRANSACTION を省略できま
す。たとえば、次のプログラムでは、SELECT 文によりデフォルトトランザクションが自
動的に起動し、処理が行われます。
. . .
EXEC SQL
SELECT * FROM CITIES
WHERE POPULATION > 4000000
4-2 埋 め 込 み S Q L ガ イ ド
デフォルトトランザクションの起動
ORDER BY POPULATION, CITY;
. . .
このように、トランザクションが 1 つのプログラムでは SET TRANSACTION を記述する
必 要 は あ り ま せ ん。た だ し、トランザクションの動作を指定するときは
SET
TRANSACTION を記述する必要があります。また、DSQL プログラムで、gpre -m スイッ
チにより前処理を行う場合にも、SET TRANSACTION の記述が必要です。
gpre による前処理時には、SET TRANSACTION 文はなくても、SELECT などのように
トランザクションを必要とする文が見つかった場合、gpre により自動的にデフォルトトラ
ンザクションが生成されます。ただし、gpre に -m スイッチが付けられている場合は、デ
フォルトトランザクションの生成は行われません。また、前処理時には、トランザクション
の動作としては、事前に指定されているデフォルトの動作が採用されます。実行時には、同
一のデータに対して別のトランザクションからアクセスがあった場合、このデフォルトの
動作により対応が行われることになります。
重要
DSQL プログラムでトランザクションを使用する場合は、必ず SET TRANSACTION を記
述しなければなりません。この場合、gpre -m スイッチにより前処理を行います。-m スイッ
チを付加したときは、デフォルトトランザクションの自動生成は行われません。したがっ
て、前処理の際、SET TRANSACTION によるトランザクションの指定がないときにはエ
ラーが報告されます。
変更が可能なトランザクション動作の詳細は、4-7 ページの「SET TRANSACTION で
の動作の指定」を参照してください。gpre -m スイッチの使い方の詳細は、第 14 章「前処
理、コンパイル、リンク」を参照してください。
SET TRANSACTION によるデフォルトトランザクションの起動
SET TRANSACTION をパラメータなしで記述した場合、デフォルトトランザクション
(GDS__TRANS)が起動します。デフォルトの動作は次のとおりです。
READ WRITE WAIT ISOLATION LEVEL SNAPSHOT
次の表は、パラメータごとに設定と動作をまとめたものです。
表 4.2
メモ
デフォルトトランザクションのデフォルト動作
パラメータ
設定
用途
アクセスモード
READ WRITE
アクセスモード。この設定では、トランザクションで
データの選択、挿入、更新、削除が可能になります。
ロック対応
WAIT
ロック対応。この設定では、トランザクションの実行
時、テーブルと列がロックされている場合に待機状態
に入ります。その後、ロックが解放されると更新が行
われます。したがって、すぐにロックの競合エラーが
報告されることはありません。
排他レベル
ISOLATION LEVEL
SNAPSHOT
この設定では、データベースのビューはトランザク
ションの起動時のまま維持されます。したがって、別
のトランザクションによりデータベースに変更が加え
られても、データベースのビューは変わりません。
デフォルトトランザクションを明示的に起動するのは優れたプログラミング手法と言えま
す。この方が、プログラムのソースコードが理解しやすくなります。
第 4 章 トランザクションの操作
4-3
名前付きトランザクションの起動
次の文は同じ意味を持ちます。どちらの場合でも、デフォルトトランザクションがデフォル
トの動作で起動されます。
EXEC SQL
SET TRANSACTION;
EXEC SQL
SET TRANSACTION NAME gds__trans READ WRITE WAIT ISOLATION LEVEL
SNAPSHOT;
デ フ ォ ル ト ト ラ ン ザ ク シ ョ ン の デ フ ォ ル ト の 動 作 を 変 更 し て 起 動 す る 場 合、SET
TRANSACTION を使用して、デフォルトとは異なる動作を指定しなければなりません。
特に指定がない場合は、デフォルトの動作が使用されます。たとえば、次の文は、アクセス
モードとして READ ONLY を使ってトランザクションを起動する例です。この場合、
READ ONLY 以外は指定していないので、ロック対応としては WAIT が、排他レベルと
しては ISOLATION LEVEL SNAPSHOT が使用されます。
EXEC SQL
SET TRANSACTION READ ONLY;
上の例でわかるように、デフォルトトランザクションを起動するときには NAME 句を省略
することができます。
重要
DSQL では、PREPARE や EXECUTE に SET TRANSACTION を付加してデフォルトト
ランザクションの動作を変更できます。記述方法は、SET TRANSACTION の場合とほぼ
同じです。デフォルトトランザクションの動作を変更しているときは、gpre -m スイッチ
を使って前処理を行う必要があります。
-m スイッチを使った前処理の詳細は、第 14 章「前処理、コンパイル、リンク」を参照し
てください。トランザクション動作と変更の詳細は、4-7 ページの「SET TRANSACTION
での動作の指定」を参照してください。
名前付きトランザクションの起動
1 つのアプリケーションで複数のトランザクションを同時に起動することができます。
InterBase では、トランザクション操作文やデータ操作文が多数用意されており、トランザ
クション名を付けることができます。複数のトランザクションが動作中の場合、トランザ
クション名によりトランザクションが識別され、トランザクションと文との関係が保たれ
ます。
同時に複数のトランザクションを使用するプログラムでは、トランザクション名を使って
他のトランザクションとの識別を行う必要があります。また、あるトランザクションが動作
中に別のトランザクションを起動する場合は、一意なトランザクション名および SET
TRANSACTION 文が個別に必要になります。名前付きトランザクションの場合も、SET
TRANSACTION でパラメータを使って動作の指定が可能です。
プログラムで名前付きトランザクションを使用する場合、処理の手順は次のようになりま
す。
1
各トランザクション名を一意な変数で宣言します。C または C++ では、トランザク
ション名は long 型ポインタとして宣言します。
2
各トランザクション名を 0 に初期化します。
4-4 埋 め 込 み S Q L ガ イ ド
名前付きトランザクションの起動
3 SET TRANSACTION と有効なトランザクション名を使って各トランザクションを起
動します。
4
重要
後続のトランザクション操作文やデータ操作文にトランザクション名を記述します。こ
の場合、文とトランザクションとの関連が切れないようにします。
DSQL 文では、名前付きトランザクションの扱いが多少異なります。詳細は、4-29 ページ
の「DSQL での複数のトランザクションの使い方」を参照してください。
複数のトランザクションのプログラムの作成の詳細は、4-26 ページの「複数のトランザク
ションの使い方」を参照してください。
トランザクションの命名
トランザクション名はプログラマが変数で設定します。SQL 文では、この変数を使ってト
ランザクションを識別します。なお、トランザクション制御やデータ操作関連の SQL 文で
トランザクション名を使用しなかった場合は、その文はデフォルトトランザクション
(GDS__TRANS)に対して働くことになります。
次は、データ型 isc_tr_handle を使用して、2 つのトランザクション名の宣言および初期化
を行う C のコード例です。SET TRANSACTION 文では、続いてこれらのトランザクショ
ンの起動も行います。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
isc_tr_handle t1, t2; /* トランザクション名を宣言する */
EXEC SQL
END DECLARE SECTION;
. . .
t1 = t2 = (isc_tr_handle) NULL; /* 名前を 0 に初期化します */
. . .
EXEC SQL
SET TRANSACTION NAME t1; /* デフォルトの動作で trans を起動します */
EXEC SQL
SET TRANSACTION NAME t2; /* デフォルトの動作で trans2 を起動します */
. . .
このコードの各手順の詳細は、以下の節で説明します。
上記のように宣言したトランザクション名は、データ操作文やトランザクション操作文の
中にオプションパラメータとして含めることができます。なお、複数のトランザクションを
扱うプログラムでトランザクション名を省略した場合、その文ではデフォルトトランザク
ション(GDS__TRANS)が使用されます。
データ操作文でトランザクション名を使用する方法の詳細は、第 6 章「データ処理」を参
照してください。
第 4 章 トランザクションの操作
4-5
名前付きトランザクションの起動
トランザクション名の宣言
トランザクションを使用するには、事前にトランザクション名を宣言しておかなければな
りません。この場合、トランザクション名は、ホスト言語のポインタとして宣言します。な
お、C または C++ では、トランザクション名は long 型ポインタで宣言しなければなりま
せん。
次は、2 つのトランザクション名を宣言しているコード例です。
EXEC SQL
BEGIN DECLARE SECTION;
isc_tr_handle t1;
isc_tr_handle t2;
EXEC SQL
END DECLARE SECTION;
メモ
上記の例では、DECLARE
SECTION でトランザクション名の宣言を行っています。
InterBase では、トランザクション名の宣言は必ずしも DECLARE SECTION 中で行う必
要はありません。ただし、SQL ではトランザクション名の他にも変数としてセクションを
宣言しておかなければならないものもあります。これらを考慮すると、互換性の点で、
DECLARE SECTION でトランザクション名を宣言しておくことをお勧めします。
DECLARE SECTION で宣言したトランザクション名は、通常、モジュールレベルでグ
ローバルに宣言されます。その他、ローカルに宣言することもできますが、その場合は次の
点に注意します。
• 名前を宣言したトランザクションは、そのモジュール内だけで使用するようにします。 ま
た、エラーが発生したとき、トランザクションによる処理をロールバックするためのエラー
処理ルーチンを収めておきます。 エラー処理には ROLLBACK を使用できます。
ROLLBACK によりトランザクション名が解放され、値に NULL が設定されます。
• 宣言したトランザクション名は、そのモジュールの外では使用しないようにします。
上記のように、トランザクション名は宣言したモジュール内で有効ですが、別のモジュー
ルから参照する必要があるときは、トランザクション名を外部変数として宣言しておきま
す。たとえば、t1 と t2 という 2 つのトランザクション名を外部変数として宣言する場合、
C では次のようになります。
EXEC SQL
BEGIN DECLARE SECTION;
extern isc_tr_handle t1, t2;
EXEC SQL
END DECLARE SECTION;
トランザクション名の初期化
トランザクション名の宣言が終わったら、トランザクション名を 0 に初期化します。この
初期化により、トランザクション名が実際に使用できるようになります。たとえば、次は、
2 つのトランザクション名を初期化する C のコード例です。
/* トランザクション名を 0 に初期化します */
t1 = t2 = (isc_tr_handle) NULL;
上記のようにトランザクション名の宣言と初期化が終了すると、次のような処理を行うこ
とができるようになります。
4-6 埋 め 込 み S Q L ガ イ ド
名前付きトランザクションの起動
• 名前によるトランザクションの起動: 複数のトランザクションを同時に使用するプログラ
ムでは、トランザクション名を使ってなんらかの処理を行うことになります。なお、デフォ
ルトトランザクションでは、名前の指定は必要ありません。
• データ操作文で使用するトランザクションの指定: 複数のトランザクションを使用するプ
ログラムでは、データ操作文でトランザクション名を指定する必要があります。ただし、
デフォルトトランザクションを使用する場合は、その必要はありません。
• 複数のトランザクションを使用するプログラムでは、トランザクション名を指定して、そ
の処理をコミットまたはロールバックします。
SET TRANSACTION での動作の指定
名前付きトランザクションは、SET TRANSACTION を使って起動します。この場合、そ
のトランザクションの動作を指定することもできます。名前付きトランザクションをデ
フォルトの動作のまま起動する場合は、次のような構文になります。
SET TRANSACTION NAME name;
SET TRANSACTION にパラメータを付けないでトランザクションを起動した場合、デ
フォルトの動作が有効になります。この場合のデフォルトの動作についての概要は、4-3 の
表 4.2 「デフォルトトランザクションのデフォルト動作」を参照してください。たとえば、
次の 2 つの文は内容的には同じです。いずれも、t1 という名前のトランザクションがデ
フォルトの動作で起動されます。
EXEC SQL
SET TRANSACTION NAME t1;
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE WAIT ISOLATION LEVEL SNAPSHOT;
次の表は、デフォルトのトランザクションの動作を指定する SET TRANSACTION パラ
メータ(オプション)の一覧です。
第 4 章 トランザクションの操作
4-7
名前付きトランザクションの起動
表 4.3
SET TRANSACTION のパラメータ
パラメータ
設定
用途
アクセス
モード
READ ONLY または READ WRITE
このパラメータを使用して、トランザクションのテーブ
ルに対する動作を指定できます。アクセスモードについ
ては、4-9 ページの「アクセスモード」を参照してくだ
さい。
ロック対応
WAIT または NO WAIT
更新や削除の際に行がロックされていた場合、そのロッ
クに対する対応方法を選択します。WAIT を指定した場
合は、ロックが解除されるのを待って更新が行われます。
一方、NO WAIT では、即座にロックの競合エラーメッ
セージが出されます。ロック対応については、4-16 ペー
ジの「ロック対応」を参照してください。
排他レベル
SNAPSHOT を指定しておくと、トランザク
ションの起動時のデータベースのビューが
そのまま維持されます。他の動作中のトラン
ザクションにより更新が行われても、ビュー
は変更されません。
現在アクセスしているテーブルに対して別のトランザク
ションからアクセスがあった場合、そのアクセスに対し
てどのように対処するかを指定します。
SNAPSHOT TABLE STABILITY を指定
した場合、現在、トランザクションにより読
み取りや更新が実行されているテーブルに
対して、他のトランザクションによる変更が
禁じられます。ただし、テーブルの列の読み
取りだけは、他のトランザクションから行う
こともできます。
READ COMMITTED を指定しておくと、
更新や削除時に、最後にコミットされた列を
使って読み取りが行われます(ロック対応と
してデフォルトの WAIT が指定されている
場合)。また、更新時、他のトランザクショ
ンと競合が起こらなければ、変更することも
できます。
READ COMMITTED 排他レベルでは、読み取る行の
バージョンを指定できます。次の 2 つのオプションが用
意されています。
• RECORD_VERSION: 必ず、最後にコミットされた
行を使って読み取りが行われます。つまり、コミッ
トされていないが、それより新しい行がディスクに
置かれている場合でも、最後にコミットされた行を
使って読み取りが実行されます。
• NO RECORD_VERSION: コミットされたかどうか
にかかわりなく、最後の行を使って読み取りが行わ
れます。また、NO RECORD_VERSION と併せて
WAIT が指定されている場合、最後の行についてコ
ミットまたはロールバックが行われるまでいったん
待機し、その後、読み取りが行われます。NO WAIT
を指定し、コミットされた行が最新のバージョンで
ない場合、トランザクションは即座にエラー(デッ
ドロック)を返します。
テーブル
予約
RESERVING
トランザクションで利用可能なテーブルを指定してロッ
クします。このロックを行った直後、ロックしたテーブ
ルには、他のトランザクションはアクセスできなくなり
ます。
データベース
指定
USING
トランザクションがアクセスできるデータベースを限定
します。したがって、ここで限定した以外のデータベー
スには、このトランザクションではアクセスできなくな
トランザクショ
ります。この USING を使用することで、
ンで必要になるシステムリソースの量を減らすことがで
きます。
メモ:USING は、DSQL では使用できません。
4-8 埋 め 込 み S Q L ガ イ ド
名前付きトランザクションの起動
SET TRANSACTION の構文は次のとおりです。
EXEC SQL
SET TRANSACTION [NAME name]
[READ WRITE| READ ONLY]
[WAIT | NO WAIT]
[[ISOLATION LEVEL] {SNAPSHOT [TABLE STABILITY]
| READ COMMITTED [[NO] RECORD_VERSION]}]
[RESERVING <reserving_clause>
| USING dbhandle [, dbhandle ...]];
<reserving_clause> = table [, table ...]
[FOR [SHARED | PROTECTED] {READ | WRITE}] [, <reserving_clause>]
以下の節では、トランザクションオプションの詳細について説明します。
アクセスモード
アクセスモードでは、そのトランザクションで可能になるテーブルへのアクセス方法を指
定できます。設定できるアクセスモードは次の 2 種類です。
• READ ONLY を指定した場合、そのトランザクションでテーブルデータの選択(読み取
り)はできますが、テーブルへのデータの挿入、更新、削除はできません。
• READ WRITE を指定した場合、そのトランザクションでテーブルデータの選択、挿入、
更新、削除のいずれも可能になります。デフォルトはこの設定で、記述しないときにはこ
の設定が使用されます。
InterBase では、トランザクションはデータの読み取りと書き込みの両方が可能なことを前
提としています。したがって、SET TRANSACTION 文で READ WRITE が省略されてい
ても、トランザクションは、読み取りと書き込みが可能な状態で起動されるようになって
います。たとえば、次の 2 つの SET TRANSACTION では、いずれも t1 というトランザ
クションが READ WRITE で起動されます。
EXEC SQL
SET TRANSACTION NAME t1;
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE;
ヒント
アクセスモードは、デフォルトの READ WRITE を使用する場合であっても、指定するこ
とが理想的なプログラミング方法です。これにより、アプリケーションのソースコードが読
みやすくなり、デバッグもしやすくなります。プログラムの意図は明確に記述するのが原
則です。
READ ONLY は、データの読み取りだけが必要なときに使用します。READ ONLY は、
READ WRITE と違って、必ず記述しなければなりません。たとえば、t1 というトランザ
クションを読み取り専用で起動する場合は、次のように記述します。
EXEC SQL
SET TRANSACTION NAME t1 READ ONLY;
第 4 章 トランザクションの操作
4-9
名前付きトランザクションの起動
排他レベル
排他レベルでは、トランザクションによりテーブルにアクセスが行われる場合のトランザ
クションの独立性(隔離の程度)を指定できます。このパラメータでは、次の事項が制御さ
れることになります。
• トランザクションから見ることのできるデータベースのビュー
• テーブルへのアクセス方法、および別のトランザクションによるテーブルへのアクセス方
法
InterBase で指定できる排他レベルは次の 3 種類です。
表 4.4 ISOLATION LEVEL のオプション
排他レベル
用途
SNAPSHOT
デフォルトの排他レベルで、この設定では、トランザクションが起動し
たときのデータベースのビューが用意されます。このビューは、コミッ
ト済みのビューで変更されることはありません。データベースの行に対
しては、他の動作中のトランザクションからも更新と挿入が可能です
が、その変更はビューに一切影響しません。この設定で起動されたトラ
ンザクションにとっては、トランザクションの起動時に保存されていた
行が現在見える行、つまり読み取り可能な行ということになります。ま
た、この設定で起動されたトランザクションによって行の更新や削除が
試みられた場合、その行が別のトランザクションによってすでに変更が
加えられていた場合は、更新競合が報告されます。
SNAPSHOT TABLE
STABILITY
この設定では、この設定で起動されたトランザクション以外のトランザ
クションは、同一のテーブルに対して挿入や更新、削除ができなくなり
ます。ただし、テーブルの行の読み取りだけは、他のトランザクション
から行うこともできます。
READ COMMITTED
この設定では、データベース中のコミット済みデータ全部に対して読み
取りが可能になります。また、他のトランザクションによって更新、コ
ミットされた行に対しても更新が可能です。この場合、更新はコミット
済みデータに対して行われるため、更新喪失は起こりません。
トランザクションの排他レベルには、SNAPSHOT か READ COMMITTED を使用するよ
うにします。どちらの設定も、同じデータベースのデータに対して複数のトランザクション
で同時に選択、挿入、更新、削除が可能です。また、ロックの競合の確率も最小限に抑え
ることができます。ロックの競合が発生する状況としては次の 2 つがあります。
• 別のトランザクションによりすでに更新や削除が行われた行に対して、さらに更新を実行
した場合。SNAPSHOT で起動されたトランザクションで発生するロックの競合が、この
ケースに該当します。これは、InterBase では、更新が行われた行がロックされるため引き
起こされます。ロックされた行は、その更新を行ったトランザクションによりコミットま
たはロールバックが完了した時点で、別のトランザクションによる更新が可能になります。
一方、排他レベルを READ COMMITTED に設定したトランザクションでは、別のトラ
ンザクションによって行の更新が実行された場合でも、その後、コミットされた時点で同
じ行の読み取りや更新が行われます(デフォルトの WAIT と NO RECORD_VERSION が
指定されている場合)
。
• SNAPSHOT TABLE STABILITY によってロックされているテーブルの行に対して、別
のトランザクションから挿入、更新、削除が試みられた場合。 SNAPSHOT TABLE
STABILITY を設定したときは、テーブル全体がロックされて書き込みはできなくなりま
4-10 埋 め 込 み S Q L ガ イ ド
名前付きトランザクションの起動
す。このため、他のトランザクションから更新要求があった場合、ロックの競合が発生す
ることになります。なお、SNAPSHOT や READ COMMITTED が設定されているトラ
ンザクションからの読み取りは可能です。
SNAPSHOT TABLE STABILITY を使用することで、テーブルの変更が可能なトランザ
クションを 1 つに絞ることができます。ただし、テーブルはロックされるため、複数のト
ランザクションから同時にアクセスが行われるような環境では、ロックの競合の確率が増
すことになります。ロックの競合の可能性の詳細は、4-16 ページの「排他レベルの相互作
用」を参照してください。
SNAPSHOT、READ COMMITED、
および SNAPSHOT TABLE STABILITY の安定度の相違
トランザクション操作文に関連して発生する問題としては、基本的に次の 5 つがあります。
このような問題に対しては、排他レベルを使って文側で対処する必要があります。
• 更新喪失:別のトランザクションにより更新が行われた後、その更新が検知されないまま、
トランザクションが同じデータに対して上書きを行ってしまうことを言います。
• ダーティ読み取り:別のトランザクションにより変更が加えられ、その変更がコミットされ
ていないのに、トランザクションが読み取りを実行してしまうことを言います。
• 再現不能読み取り:あるトランザクションにより、行の更新や削除が行われている間に、別
のトランザクションが何回も読み取りを行ってしまうことを言います。つまり、読み取り
データはそのつど変わることになります。READ COMMITTED では、性質上、この問題
には対処できません。これは、READ COMMITTED が指定されているトランザクション
では、他のトランザクションによる削除が許容されているためです。
• ファントム行:別のトランザクションにより複数行の更新が行われた後、トランザクション
の読み取りで行の取りこぼし(ファントム行)が出ることを言います。
READ
COMMITTED では、性質上この問題には対処できません。
• 更新副作用:行では、通常、各値が相互に依存していますが、行によっては値同士の関連性
がロックやトリガー、整合性制約によって適宜保護または確立されていないことがありま
す。このような行に対して、複数のトランザクションによって同時にアクセスや更新が行
われ、そのアクセスや更新が不規則に繰り返し実行された場合、行の不整合を起こすこと
があります。これを更新副作用と呼んでいます。上のように、同一のデータに対して同時
に不規則に繰り返しアクセスするトランザクションは、トランザクション間のトランザク
ションと呼ばれます。
この 5 つの問題については、3 種類の排他レベルを使って対処することができます。なお、
READ COMMITTED に関しては、前述のように制限があります。次の表は、いずれかの
排他レベルが設定されているトランザクションについて、そのトランザクションがアクセ
スする行やテーブルに対して、別のトランザクションからどのような処理が可能になるか
を、上記の問題に関連させながら示したものです。
第 4 章 トランザクションの操作
4-11
名前付きトランザクションの起動
表 4.5
InterBase でのトランザクションの競合の管理
SNAPSHOT または READ
COMMITTED
SNAPSHOT TABLE STABILITY
更新喪失
このトランザクションにより更新された行に
対しては、他のトランザクションからの更新は
できません。
このトランザクションにより制御されるテー
ブルに対しては、他のトランザクションからの
更新はできません。
ダーティ読み取り
このトランザクションによって更新された行
に対しては、SNAPSHOT が設定されている別
のトランザクションから、更新前の行の読み取
りだけを行うことができます。
このトランザクションによって更新される
テーブルに対しては、他のトランザクションか
らアクセスできません。
問題
このトランザクションによって行われた更新
に対しては、READ COMMITTED が設定さ
れている別のトランザクションから、更新前の
データまたはコミット済みのデータの読み取
りだけを行うことができます。
再現不能読み取り
SNAPSHOT(または SNAPSHOT TABLE SNAPSHOT(または SNAPSHOT TABLE
STABILITY)が設定されているトランザク STABILITY)が設定されているトランザク
ションでは、そのトランザクションの起動時に
コミット済みだった行だけが読み取り可能で
す。したがって、SNAPSHOT のトランザク
ションでは再現不能読み取りは起こりません。
ションでは、そのトランザクションの起動時に
コミット済みだった行だけが読み取り可能で
す。したがって、SNAPSHOT のトランザク
ションでは再現不能読み取りは起こりません。
一方、READ COMMITTED が設定されてい
るトランザクションでは、読み取りデータが変
化することを前提としなければなりません。
このトランザクションによって更新される
テーブルに対しては、他のトランザクションか
らアクセスできません。
ファントム行
READ COMMITTED が設定されているトラ
ンザクションでは、ファントム行が起こること
があります。
このトランザクションにより制御されるテー
ブルに対しては、他のトランザクションからア
クセスできません。
更新副作用
このトランザクションにより更新された行に
対しては、SNAPSHOT が設定されている別の
トランザクションからは、更新前の行の読み取
りだけが可能です。
このトランザクションにより制御されるテー
ブルに対しては、他のトランザクションからの
更新はできません。
このトランザクションによって行われた更新
に対しては、READ COMMITTED が設定さ
れている別のトランザクションから、更新前の
データまたはコミット済みのデータの読み取
りだけを行うことができます。
トランザクション間のトランザクションにつ
いては、トリガーや整合性制約を使って問題を
回避する必要があります。
トランザクション間のトランザクションにつ
いては、トリガーや整合性制約を使って問題を
回避する必要があります。
SNAPSHOT または READ COMMITTED の選択
排他レベルとして SNAPSHOT と READ COMMITTED のどちらを選択するかは、アプ
リケーションの内容によって異なります。InterBase では、SNAPSHOT がデフォルトの排
他 レ ベ ル と な っ て い ま す。READ COMMITTED もおよそ動作は同じです。ただし、
SNAPSHOT とは異なり、他のトランザクションによる更新は、そのコミット後に、順次
読み取りが可能になります。このため、READ COMMITTED を使用することで、通常、
データの競合を減らすことができます。
4-12 埋 め 込 み S Q L ガ イ ド
名前付きトランザクションの起動
SNAPSHOT では、そのトランザクションの起動時に使用されたデータベースのビューが
そのまま維持されます。これに対して、READ COMMITTED が設定されているトランザ
クションでは、起動後、他のトランザクションによる行の更新があった場合、そのコミッ
ト直後に行の読み取り(つまり、最新の行の読み取り)が可能になります。どのトランザク
ションでも、SELECT 文を使用できます。ただし、次のような状況では SELECT による
読み取りは実行できません。
• アクセス対象のテーブルに対して、SNAPSHOT TABLE STABILITY が設定されている
別のトランザクションによって更新が実行され、そのテーブルがロックされている場合。
• 別のトランザクションにより挿入が実行され、その挿入がコミットされていない場合。 こ
の場合、SELECT は使用できますが、挿入された行の読み取りはできません。
要するに、READ COMMITTED が設定されているトランザクションでは、他のトランザ
クションによって更新された行をコミット直後に読み取ることができます。一方、
SNAPSHOT が設定されているトランザクションでは、起動後に行が更新された場合でも、
その更新の前の行(つまり、起動時に使用されたデータベースの行)の読み取りだけが可
能です。
SNAPSHOT、READ COMMITTED のどのトランザクションでも、アクセスモードが
READ WRITE の 場 合 は、INSERT、UPDATE、DELETE を使用できます。ただし、
SNAPSHOT TABLE STABILITY が設定されているトランザクションによってテーブル
がロックされている場合は、この限りではありません。
SNAPSHOT トランザクションでは、処理対象の行のうち、すでに他のトランザクション
によって更新または削除が実行され、その更新や削除がコミット済みの行に対しては、新
た な 更 新 や 削 除 は 実 行 で き ません。 このような更新、削除が行われた行に対して、
SNAPSHOT トランザクションにより再度更新が試みられたときは、更新競合エラーが発
生します。
一方、READ COMMITTED と READ WRITE が設定されているトランザクションでは、
他のトランザクションによって変更された行もコミット後に読み取りが可能ですし、その
後、このような行に対してさらに更新を実行することもできます。
なお、同一の行に対して、SNAPSHOT と READ COMMITTED の両方のトランザクショ
ンにより同時に更新が実行されることがあります。このような場合、更新競合が発生する
ことがあります。競合が起こった場合、次のような結果が予想されます。
• 大量更新または検索更新:つまり、1 つの UPDATE によりテーブルの複数行が処理される
更新では、競合が発生した時点で、それまでの更新は全部取り消されます。この場合、
UPDATE の再実行は可能です。なお、READ COMMITTED トランザクションでは、NO
RECORD_VERSION を指定することで、正しい読み取り結果に基づいた更新または削除
が可能になります。詳細は、4-14 ページの「排他レベルとして READ COMMITTED を
使用する場合のトランザクションの起動」を参照してください。
• カーソル更新または位置指定更新:つまり、行の集合から一度に 1 行ずつ取り出されて更
新が行われるような場合、競合の発生時には 1 行だけが無効になります。この場合、更新
を再開するには、カーソルをいったん閉じた後で再び開いて、競合が発生した箇所から更
新を再開します。
カーソルによる UPDATE の詳細は、第 6 章「データ処理」を参照してください。
第 4 章 トランザクションの操作
4-13
名前付きトランザクションの起動
排他レベルとして SNAPSHOT を使用する場合のトランザクションの起動
InterBase では、SNAPSHOT が排他レベルのデフォルトとなっています。したがって、排
他レベルとして SNAPSHOT を使用する場合、SET TRANSACTION で特に指定する必要
はありません。たとえば、次の 2 つの文は、いずれも同じになります。どちらも、t1 という
ト ラ ン ザ ク シ ョ ン を ア ク セ スモードとして READ WRITE を、排他レベルとして
SNAPSHOT を使って起動する例です。
EXEC SQL
SET TRANSACTION NAME t1;
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE SNAPSHOT;
なお、排他レベルは、アクセスモードおよびロック対応(記述されている場合)の次に付
加しなければなりません。
ヒント
排他モードは、デフォルトの SNAPSHOT を使用する場合であっても、指定することが理
想的なプログラミング方法です。これにより、アプリケーションのソースコードが読みやす
くなり、デバッグもしやすくなります。プログラムの意図は明確に記述するのが原則です。
排他レベルとして READ COMMITTED を使用する場合のトランザクションの
起動
排他レベルとして READ COMMITTED を使用する場合は、この排他レベルを必ず指定し
なければなりません。たとえば、次の文は、t1 というトランザクションを、アクセスモー
ドとして READ WRITE を、排他レベルとして READ COMMITTED を使って起動する
例です。
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE READ COMMITTED;
排他レベルは、必ずアクセスモードの次に記述しなければなりません。アクセスモードデ
フォルトで省略する場合は、排他レベルはトランザクション名に続けて記述します。
READ COMMITTED では、RECORD_VERSION と NO RECORD_VERSION という 2
種類のパラメータを付加することもできます。このパラメータは両方同時に使用すること
はできません。この 2 種類のパラメータを使用して、READ COMMITTED トランザク
ションがコミットされていない行にアクセスした場合の動作を指定することができます。
• RECORD_VERSION を指定した場合、必ず最後にコミットされた行に対して読み取りが
行われます。それより新しい、コミットされていない行がディスクに格納されている場合
も同じです。
• NO RECORD_VERSION を指定した場合、最後にコミットされた行ではなく、コミット
されていなくても、要求された最も最後の行に対して読み取りが行われます。この NO
RECORD_VERSION がデフォルトとなっています。なお、ロック対応として WAIT を指
定しているときは、いったん待機状態に入り、コミットされていない最も最後の行がコミッ
トまたはロールバックされてから、再度読み取りを実行します。NO WAIT がオプション
として指定されている場合は、トランザクションからただちにデッドロックエラーが報告
されます。
4-14 埋 め 込 み S Q L ガ イ ド
名前付きトランザクションの起動
上の 2 つのパラメータのうち、NO RECORD_VERSION はデフォルトなので、READ
COMMITTED で特に指定する必要はありません。たとえば、次の 2 つの文の内容は同じ
です。いずれも、t1 というトランザクションを、アクセスモードとして READ WRITE、排
他レベルとして READ COMMITTED、パラメータとして NO RECORD_VERSION を
使って起動します。
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE READ COMMITTED;
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE READ COMMITTED
NO RECORD_VERSION;
一方、RECORD_VERSION を使用する場合は、必ず文中に指定しなければなりません。た
とえば、t1 というトランザクションを、アクセスモードとして READ WRITE、排他レベ
ルとして READ COMMITTED、パラメータとして RECORD_VERSION を使って起動す
る場合、次のように記述します。
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE READ COMMITTED
RECORD_VERSION;
排他レベルとして SNAPSHOT TABLE STABILITY を使用する場合の
トランザクションの起動
排他レベルとして SNAPSHOT TABLE STABILITY を使用する場合は、この排他レベル
を必ず指定しなければなりません。次の文は、t1 というトランザクションを、アクセスモー
ドとして READ WRITE、排他レベルとして SNAPSHOT TABLE STABILITY を使って
起動する例です。
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE SNAPSHOT TABLE STABILITY;
排他レベルは、アクセスモードおよびロック対応(指定されている場合)の次に付加しな
ければなりません。
重要
SNAPSHOT TABLE STABILITY の使用には注意が必要です。この排他レベルを指定し
た場合、複数のトランザクションが同一のデータベースにアクセスする環境では、ロック
競合の発生の可能性が非常に高くなります。
第 4 章 トランザクションの操作
4-15
名前付きトランザクションの起動
排他レベルの相互作用
2 つのトランザクションにより同一のデータベースにアクセスが行われたときは、ロックの
競合が起こることがあります。この場合、2 つのトランザクションに設定されている排他レ
ベルおよびアクセスモードを考慮してみる必要があります。次の表に、競合が発生する可能
性がある組み合わせを示します。
表 4.6
排他レベルと SELECT および UPDATE との関係
SNAPSHOT または
READ COMMITTED
UPDATE
SNAPSHOT または
READ
COMMITTED
SNAPSHOT
TABLE
STABILITY
SNAPSHOT TABLE STABILITY
SELEC
T
UPDATE
SELECT
UPDATE
同時に更新が実行されると
競合する可能性がある
—
必ず競合
必ず競合
SELECT
—
—
—
—
UPDATE
必ず競合
—
必ず競合
必ず競合
SELECT
必ず競合
—
必ず競合
—
上の表に示すように、排他レベルで見ると、トランザクションは SNAPSHOT と READ
COMMITTED の組み合わせで最も競合の可能性が薄くなります。たとえば、t1 がアクセ
スモードが READ WRITE の SNAPSHOT のトランザクションで、t2 がアクセスモードが
READ WRITE の READ COMMITTED のトランザクションとすると、2 つのトランザク
ションが同一の行に対して更新した場合にのみ競合が起こることになります。さらに、t1
と t2 のトランザクションのアクセスモードが READ ONLY の場合、競合は一切発生しま
せん。
また、アクセスモードが READ WRITE の SNAPSHOT TABLE STABILITY のトランザ
クションでは、そのトランザクションだけであればテーブルの更新が可能ですが、同時に
動作するトランザクションが他にあれば、そのトランザクションと競合を生じます。ただ
し、アクセスモードが READ ONLY の SNAPSHOT または READ COMMITTED のトラ
ンザクションであれば競合は起こりません。また、アクセスモードが READ ONLY の
SNAPSHOT TABLE STABILITY のトランザクションでは、他の READ ONLY のトラン
ザクションとの競合は起こりませんが、他のトランザクションがデータの挿入、更新、削
除を実行するものであれば競合が生じます。
ロック対応
トランザクションの実行の際に、ロックの競合が起こることがありますが、ロック対応で
は、その場合の対応処置を指定することができます。ロック対応で指定できる対応措置は次
の 2 とおりです。
• WAIT:デフォルト。WAIT を指定しておくと、リソース(行やテーブル)がロックされ
ている場合、トランザクションはいったん待機状態に入ります。この後、リソースが解放
されると、再度処理が行われます。
• NO WAIT:NO WAIT を記述しておくと、ロックを見つけた場合、ロックの競合エラー
が報告されます。ロックの解放まで待機することはありません。
4-16 埋 め 込 み S Q L ガ イ ド
名前付きトランザクションの起動
WAIT はデフォルトなので、SET TRANSACTION で明示的に記述する必要はありませ
ん。したがって、次の 2 つの文の意味は同じです。どちらの文でも、t1 というトランザク
ションが、アクセスモードとして READ WRITE、ロック対応として WAIT、排他レベル
として READ COMMITTED を使って起動されます。
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE READ COMMITTED;
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE WAIT READ COMMITTED;
一方、NO WAIT を使用する場合は、SET TRANSACTION で明示的に指定しなければな
り ま せ ん。た と え ば、t1 というトランザクションを、アクセスモードとして READ
WRITE、ロック対応として NO WAIT、排他レベルとして SNAPSHOT を使って起動す
る場合は、次のように記述します。
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE NO WAIT READ SNAPSHOT;
なお、ロック対応を指定するときは、アクセスモードと排他レベルの間に記述します。
ヒント
ロック対応は、デフォルトの WAIT を使用する場合であっても、指定することが理想的な
プログラミング方法です。これにより、アプリケーションのソースコードが読みやすくな
り、デバッグもしやすくなります。プログラムの意図は明確に記述するのが原則です。
RESERVING 句
SET TRANSACTION には、RESERVING 句を付加することもできます。この
RESERVING 句を使用して、そのトランザクションで使用するテーブルを予約することが
できます。この結果、他のトランザクションからのアクセスは制限されることになります。
RESERVING では、トランザクションの起動時にテーブルの予約が行われます。この点で、
そのつどテーブルの指定が必要なデータ操作文とは異なります。RESERVING は、通常、
同一のデータベースに対して複数のトランザクションからアクセスされる環境で有効で
す。RESERVING の使用には、主に次の 3 つの利点があります。
• 処理中、特に必要な場合にロックが起こり、このためデッドロック(処理停止)や更新競
合が発生することがありますが(デフォルトの動作)
、このような更新競合などを防ぐこと
ができます。
• トリガーや整合性制約により複数のテーブルにロックがかけられることがありますが、こ
のロックを従属性ロック(dependency locking)と呼んでいます。RESERVING では、こ
の従属性ロックを強化する働きがあります。この従属性ロックは、特に明示的に保護する
必要はありませんが、それでも RESERVING を使用することにより、テーブルの間接競
合から起こる更新競合を防ぐことができます。
• トランザクションで使用するテーブルについて、他のトランザクションからのアクセスの
レベル(処理の内容)を限定することができます。たとえば、READ WRITE SNAPSHOT
のトランザクションを起動し、そのトランザクションでのみ 1 つのテーブルに対する更新
を行う場合、RESERVING 句を使用することによって、他のトランザクションからは、そ
のテーブルに対して更新ができなくなるようにすることができます。
重要
SET TRANSACTION 文では、RESERVING 句または USING 句のどちらか一方しか設定
できません。テーブルを予約してトランザクションを実行するには、SET TRANSACTION
構文を使用します。
第 4 章 トランザクションの操作
4-17
名前付きトランザクションの起動
EXEC SQL
SET TRANSACTION [NAME name]
[READ WRITE| READ ONLY]
[WAIT | NO WAIT]
[[ISOLATION LEVEL] {SNAPSHOT [TABLE STABILITY]
| READ COMMITTED [[NO] RECORD_VERSION]}]
RESERVING <reserving_clause>;
<reserving_clause> = table [, table ...]
[FOR [SHARED | PROTECTED] {READ | WRITE}] [, <reserving_clause>]
RESERVING 句でテーブルを複数予約するときは、同じテーブル名を記述しないようにし
ます。また、テーブル名はカンマで区切って並べます。テーブル名の後ろには、その予約の
オプション(PROTECTED READ など、他のトランザクションで可能な処理内容)を記
述します。RESERVING 句で指定できるオプションと処理内容は次の表のとおりです。
表 4.7
RESERVING 句を使用したテーブル予約のオプション
予約オプション
用途
PROTECTED
READ
予約したテーブルの行に対しては、他のトランザクションによる更新はできま
せん。テーブルの読み取りは、どのトランザクションから行うこともできます。
PROTECTED
WRITE
予約したテーブルの行に対しては、他のトランザクションによる更新はできま
せん。
SNAPSHOT または READ COMMITTED が設定されているトランザクショ
ンからテーブルの読み取りは可能ですが、更新はできません。
SHARED READ
どのトランザクションからでも、テーブルの読み取りが可能です。READ
WRITE が設定されているトランザクションからは、テーブルの更新が可能で
す。この SHARED READ が、最も解放されたモードです。
SHARED WRITE
SNAPSHOT または READ COMMITTED のトランザクションのうち、アク
セスモードが READ WRITE のトランザクションからは、テーブルの更新が可
能です。SNAPSHOT または READ COMMITTED のトランザクションでは、
テーブルの読み取りが可能です。
たとえば、トランザクション名が t1、アクセスモードとして READ WRITE、排他レベル
として SNAPSHOT という設定で、EMPLOYEE というテーブルを予約する場合、次の文
のようになります。ここでは、予約オプションとして PROTECTED WRITE を指定してい
ます。
EXEC SQL
SET TRANSACTION NAME t1 READ WRITE WAIT SNAPSHOT
RESERVING EMPLOYEE FOR PROTECTED WRITE;
また、トランザクション名が t1、アクセスモードとして READ WRITE、排他レベルとし
て READ COMMITTED という設定で、EMPLOYEES、EMP_PROJ という 2 つのテー
ブルを予 約する場合、次の 文のようになります。ここでは、予約オプションとして、
EMPLOYEES には SHARED WRITE を、EMP_PROJ には PROTECTED WRITE をそ
れぞれ指定しています。
EXEC SQL
4-18 埋 め 込 み S Q L ガ イ ド
名前付きトランザクションの起動
SET TRANSACTION NAME t1 READ WRITE WAIT READ COMMITTED
RESERVING EMPLOYEES FOR SHARED WRITE, EMP_PROJ
FOR PROTECTED READ;
SNAPSHOT または READ COMMITTED のトランザクションに RESERVING を加える
ことで、テーブルを予約し、そのテーブルに対して別のトランザクションから同時にアク
セスすることを制限することができます。また、SNAPSHOT TABLE STABILITY のトラ
ンザクションでは、RESERVING を使用することにより、競合などが起こった場合のデッ
ドロックの発生率を低くすることができます。
USING 句
SET TRANSACTION によりトランザクションが起動されるたびに、その時点で開かれて
いるデータベース全部にシステムリソースが割り当てられます。このような場合、USING
句を使用して、トランザクションのアクセス対象となるデータベースの数を限定し、シス
テムリソースを節約することができます。つまり、USING を記述することで、開かれてい
るデータベースのうち、指定したデータベースのテーブルだけにアクセスが行われるよう
になります。USING の構文は次のとおりです。
EXEC SQL
SET TRANSACTION [NAME name]
[READ WRITE | READ ONLY]
[WAIT | NO WAIT]
[[ISOLATION LEVEL] {SNAPSHOT [TABLE STABILITY]
| READ COMMITTED [[NO] RECORD_VERSION]}]
USING dbhandle> [, dbhandle ...];
重要
SET TRANSACTION 文では、1 つの文につき、RESERVING 句または USING 句のどち
らかしか設定できません。
次の C のコード例では、test.ib、research.ib、employee.ib という 3 つのデータベースにそ
れぞれ TEST、RESEARCH、EMP というデータベースハンドルを設定して開いています。
その後、デフォルトトランザクションを起動するとともに、USING を使ってアクセス対象
のデータベースを TEST と EMP の 2 つに限定しています。
. . .
EXEC SQL
SET DATABASE ATLAS = 'test.ib';
EXEC SQL
SET DATABASE RESEARCH = 'research.ib';
EXEC SQL
SET DATABASE EMP = 'employee.ib';
EXEC SQL
CONNECT TEST, RESEARCH, EMP; /* すべてのデータベースを開きます */
EXEC SQL
SET TRANSACTION USING TEST, EMP;
. . .
第 4 章 トランザクションの操作
4-19
データ操作文でのトランザクション名の扱い
データ操作文でのトランザクション名の扱い
名前付きトランザクションの起動後は、INSERT、UPDATE、DELETE、OPEN の各デー
タ操作文で、その名前を使用することができます。トランザクション名を指定した文は、以
後、そのトランザクションに制御されるとともに、関連して機能することになります。たと
えば、次の C コードでは、mytrans1 と mytrans2 という 2 つのトランザクションハンドル
を宣言するとともに、ハンドルを 0 に初期化した後、この 2 つのトランザクションを起動
しています。次に、INSERT と OPEN で各トランザクションを指定しています。以後、
INSERT と OPEN はいずれも、指定したトランザクションと関連して動作することになり
ます。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
long *mytrans1, *mytrans2;
char city[26];
EXEC SQL
END DECLARE SECTION;
mytrans1 = 0L;
mytrans2 = 0L;
. . .
EXEC SQL
SET DATABASE ATLAS = 'atlas.ib';
EXEC SQL
CONNECT;
EXEC SQL
DECLARE CITYLIST CURSOR FOR
SELECT CITY FROM CITIES
WHERE COUNTRY = 'Mexico';
EXEC SQL
SET TRANSACTION NAME mytrans1;
EXEC SQL
SET TRANSACTION mytrans2 READ ONLY READ COMMITTED;
. . .
printf('Mexican city to add to database: ');
gets(city);
EXEC SQL
INSERT TRANSACTION mytrans1 INTO CITIES (CITY, COUNTRY)
VALUES :city, 'Mexico';
EXEC SQL
COMMIT mytrans1;
EXEC SQL
OPEN TRANSACTION mytrans2 CITYLIST;
EXEC SQL
FETCH CITYLIST INTO :city;
while (!SQLCODE)
4-20 埋 め 込 み S Q L ガ イ ド
トランザクションの終了
{
printf("%s¥n", city);
EXEC SQL
FETCH CITYLIST INTO :city;
}
EXEC SQL
CLOSE CITYLIST;
EXEC SQL
COMMIT;
EXEC SQL
DISCONNECT;
. . .
上の例に示すように、DECLARE CURSOR 文にはトランザクション名を記述しません。ト
ランザクション名は、宣言したカーソルの OPEN 文に記述します。これで、宣言したカー
ソルとトランザクション名が関連付けられます。宣言したカーソルに関連する後続の
FETCH 文と CLOSE 文には、トランザクション名の指定は不要です。
メモ
トランザクション名は、DSQL の EXECUTE と EXECUTE IMMEDIATE の各ステート
メントでも使用できます。
データ操作文でトランザクション名を使用する方法の詳細は、第 6 章「データ処理」を参
照してください。トランザクション名と COMMIT 文の詳細は、4-22 ページの「COMMIT
の使い方」を参照してください。DSOL 文でトランザクション名を使用する方法の詳細は、
4-29 ページの「DSQL での複数のトランザクションの使い方」を参照してください。
トランザクションの終了
トランザクションで行うタスクが全部終了したとき、または、エラーが発生してタスクの
完了が不可能になった場合は、トランザクションを終了する必要があります。これにより、
データベースは通常の状態に戻ります。トランザクションは、次の 2 つの文を使って終了
することができます。
• COMMIT:この文では、トランザクションで実行された変更がデータベースに保存され
ます。つまり、トランザクションでの全処理が無事に終了したことがシステムに報告され
ます。
• ROLLBACK:この文では、トランザクションによる変更が取り消され、データベースは
トランザクションが起動する前の状態に戻ります。この ROLLBACK は、通常、なんらか
のエラーが発生し、トランザクションによるタスクの完了が不可能な場合に使用します。
COMMIT でも ROLLBACK でも、現在起動されているトランザクションに関係するレ
コードストリームが閉じられます。また、トランザクション名が 0 に初期化されるととも
に、そのトランザクションに割り当てられていたシステムリソースが解放されます。解放さ
れたシステムリソースは、この後、アプリケーションやプログラムに利用されることにな
ります。
第 4 章 トランザクションの操作
4-21
トランザクションの終了
COMMIT や ROLLBACK の利用には、この他にも利点があります。つまり、これらの文
により、プログラムの論理構成や意図が明確になり、プログラムも読みやすくなります。ま
た、何より、プログラマは、トランザクションによる変更が意図したとおりに処理されて
いるかどうかをその記述から判断することができます。
ROLLBACK は、通常、エラー発生時にトランザクションを終了処理するための内部エラー
処理ルーチンに使用されます。また、なんらかの理由でトランザクションの処理が完了しな
かった場合、処理の再実行に先立ってロールバックする場合にも利用できます。さらに、プ
ログラムが復旧不能なエラーに陥ったときに、データベースを元の状態に復元する目的で
使用することもできます。
重要
プログラムがトランザクションの終了の前に終了したときは、トランザクションの処理は
自動的にロールバックされます。ただし、データベースは自動的に閉じることはありませ
ん。このように、データベースが閉じられないままプログラムが終了してしまうと、データ
の損失や破損が起こることがあります。このため、開かれているデータベースは、必ず、
DISCONNECT、COMMIT RELEASE、ROLLBACK RELEASE 文を使用して、事前に
閉じておかなければなりません。
DISCONNECT、COMMIT RELEASE、および ROLLBACK RELEASE の詳細は、第 3
章「データベースの操作」を参照してください。
COMMIT の使い方
COMMIT を使用して、それまでのトランザクションによる変更をデータベースに保存す
ることができます。
COMMIT を実行すると、トランザクションに関連するレコードストリームが閉じられる
と同時に、トランザクション名が 0 に初期化されます。また、トランザクションに割り当
てられていたシステムリソースが解放され、他の目的に利用できるようになります。
COMMIT の正規の構文は次のとおりです。
EXEC SQL
COMMIT [TRANSACTION name] [RETAIN [SNAPSHOT] | RELEASE dbhandle
[, dbhandle ...]]
たとえば、次の C コードは、1 つのトランザクションが終了するまでの例です。このコー
ドでは、1993 年 1 月 1 日より前に入社した従業員全員に対して、生活費の上昇を補填する
ため 4.3% の賃金アップを行っています。条件に合致した従業員のレコードの更新が無事
終了した場合、トランザクションがコミットされ、変更がデータベースに記録されます。こ
の変更は、データベースにも実際に記録されます。
. . .
EXEC SQL
SET TRANSACTION SNAPSHOT TABLE STABILITY;
EXEC SQL
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.043
WHERE HIRE_DATE < '1-JAN-1993';
EXEC SQL
COMMIT;
. . .
4-22 埋 め 込 み S Q L ガ イ ド
トランザクションの終了
なお、COMMIT でトランザクション名を指定しなかったときは、COMMIT はデフォルト
トランザクションである GDS__TRANS に対して働きます。したがって、デフォルトトラ
ンザクション以外のトランザクションによる処理をコミットする場合は、そのトランザク
ション名をパラメータとして指定します。
ヒント
アクセスモードが READ ONLY のトランザクションでは、データベースの変更は行われま
せんが、トランザクションの終了には ROLLBACK ではなく COMMIT を使用してくださ
い。これは、COMMIT の方が、次のトランザクションを起動する際のオーバーヘッドが大
幅に少なくて済むためです。
トランザクション名の指定
デフォルトトランザクションの処理をコミットする場合は COMMIT にトランザクション
名の指定は不要ですが、それ以外のときは COMMIT にトランザクション名を記述しなけ
ればなりません。たとえば、次は、名前を使って 2 つのトランザクションを起動し、各ト
ランザクションによる変更をコミットする C のコードです。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
isc_tr_handle TR1, TR2;
EXEC SQL
END DECLARE SECTION;
TR1 = (isc_tr_handle) NULL;
TR2 = (isc_tr_handle) NULL;
. . .
EXEC SQL
SET TRANSACTION NAME TR1;
EXEC SQL
SET TRANSACTION NAME TR2;
. . .
/* 実際の処理を行います */
. . .
EXEC SQL
COMMIT TRANSACTION TR1;
EXEC SQL
COMMIT TRANSACTION TR2;
. . .
重要
複数のトランザクションを使用するプログラムでは、COMMIT には必ずトランザクショ
ン名を記述しなければなりません。ただし、デフォルトトランザクションをコミットする
場合は、記述する必要はありません。
トランザクションを解放しないコミット
ときは、トランザクションによる変更を保存した後でも、現在のトランザクションのスナッ
プショットを解除したくない場合もあります。このような場合、COMMIT に RETAIN オ
プションを付加します。COMMIT RETAIN を使用すると、作業がコミットされ新しいト
ランザクションが開かれます。このとき、これまでのトランザクションのスナップショッ
トは維持されます。特に、複数のユーザー環境で使用するような負荷の大きいシステムで使
第 4 章 トランザクションの操作
4-23
トランザクションの終了
用する場合に、これまでのスナップショットが維持されると、処理速度の向上につながり
ます。また、トランザクションを終了し、別のトランザクションを起動する場合に比べて、
システムリソースの消費も節約できます。ただし、他のユーザーからのアクセスが保留され
ていてもわからないというのが COMMIT に RETAIN を付加した場合の欠点です。
RETAIN の構文は、次のようになります。
EXEC SQL
COMMIT [TRANSACTION name] RETAIN [SNAPSHOT];
ヒント
Delphi などのツールで BDE を使用している場合にこの機能が必要なときは、BDE 環境設
定で「soft commit」を指定してください。
たとえば、次の例は、CITIES テーブルの POPULATION 列の値を更新するための C 言語
のコードです。国名と人口増加割合は、いずれもユーザーが入力します。コードでは、行が
更新されるたびに、RETAIN 付きの COMMIT が実行され、現在のカーソルとシステムリ
ソースが維持されます。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
char country[26], city[26], asciimult[10];
int multiplier;
long pop;
EXEC SQL
END DECLARE SECTION;
. . .
main ()
{
EXEC SQL
DECLARE CHANGEPOP CURSOR FOR
SELECT CITY, POPULATION
FROM CITIES
WHERE COUNTRY = :country;
printf("Enter country with city populations needing adjustment: ");
gets(country);
EXEC SQL
SET TRANSACTION;
EXEC SQL
OPEN CHANGEPOP;
EXEC SQL
FETCH CHANGEPOP INTO :city, :pop;
while(!SQLCODE)
{
printf("City: %s Population: %ld¥n", city, pop);
printf("¥nPercent change (100%% to -100%%:");
gets(asciimult);
multiplier = atoi(asciimult);
EXEC SQL
4-24 埋 め 込 み S Q L ガ イ ド
トランザクションの終了
UPDATE CITIES
SET POPULATION = POPULATION * (1 + :multiplier / 100)
WHERE CURRENT OF CHANGEPOP;
EXEC SQL
COMMIT RETAIN; /* 変更をコミットし、現在の状態を保存します */
EXEC SQL
FETCH CHANGEPOP INTO :city, :pop;
if (SQLCODE && (SQLCODE != 100))
{
isc_print_sqlerror(SQLCODE, isc_$status);
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT;
exit(1);
}
}
EXEC SQL
COMMIT;
EXEC SQL
DISCONNECT;
}
メモ
なお、COMMIT RETAIN の後に ROLLBACK を実行した場合、その COMMIT RETAIN
の後に実行された更新と書き込みだけが ROLLBACK によりロールバックされます。
重要
複数のトランザクションを扱うプログラムでは、COMMIT RETAIN でも必ずトランザク
ション名を指定しなければなりません。ただし、トランザクションがデフォルトの場合は、
その必要はありません。トランザクション名の詳細は、4-5 ページの「トランザクションの
命名」を参照してください。
ROLLBACK の使い方
ROLLBACK を使用して、トランザクションが起動する前の状態にデータベースを復元す
ることができます。また、ROLLBACK では、トランザクションに関連するレコードスト
リームを閉じたり、トランザクション名の 0 への初期化、トランザクションに割り当てら
れているシステムリソースの解放といった処理も実行されます。ROLLBACK は、通常、エ
ラー処理ルーチンで使用します。ROLLBACK の構文は次のとおりです。
EXEC SQL
ROLLBACK [TRANSACTION name] [RELEASE [dbhandle [, dbhandle ...]]];
次の C コードは、1 つのトランザクションが終了するまでの例です。このコードでは、1993
年 1 月 1 日より前に入社した従業員全員に対して、生活費の上昇を補てんするため 4.3%
の賃金アップを行います。条件に合致した従業員のレコードの更新が無事終了した場合、ト
ランザクションがコミットされ、変更がデータベースに記録されます。また、エラーが発生
したときは、トランザクションによる変更はすべて取り消され、データベースはトランザ
クションの起動前の状態に復元します。
. . .
第 4 章 トランザクションの操作
4-25
複数のトランザクションの使い方
EXEC SQL
SET TRANSACTION SNAPSHOT TABLE STABILITY;
EXEC SQL
UPDATE EMPLOYEES
SET SALARY = SALARY * 1.043
WHERE HIRE_DATE < '1-JAN-1993';
if (SQLCODE && (SQLCODE != 100))
{
isc_print_sqlerror(SQLCODE, isc_$status);
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT;
exit(1);
}
EXEC SQL
COMMIT;
EXEC SQL
DISCONNECT;
. . .
なお、ROLLBACK でトランザクション名を指定しなかったときは、ROLLBACK はデ
フォルトトランザクションである GDS__TRANS に対して働きます。したがって、デフォ
ルトトランザクション以外のトランザクションによる処理をロールバックする場合は、そ
のトランザクション名を ROLLBACK のパラメータとして指定します。
複数のトランザクションの使い方
InterBase では複数のトランザクション名が扱えるため、プログラムで同時に複数のトラン
ザクションを起動し、各トランザクションに個別に処理を実行させることができます。この
場合、トランザクション名はそれぞれ別でなければなりません。これは、複数のトランザク
ションが同時に機能する場合、その名前により識別されるためです。トランザクション名は
また、データ操作文やトランザクション操作文で使用することもできます。これらの文で
トランザクション名を指定した場合、その文は指定したトランザクションと関連して機能
することになります。トランザクション名の宣言と使用の詳細は、4-4 ページの「名前付き
トランザクションの起動」を参照してください。
名前付きトランザクションをプログラムで使用するには、次のような手順があります。
1
トランザクション名を一意な変数で宣言します。トランザクションが複数のときは、名
前が重複しないようにします。
2 トランザクション名(変数)を 0 に初期化します。
3 SET TRANSACTION と有効なトランザクション名を使って各トランザクションを起
動します。
4-26 埋 め 込 み S Q L ガ イ ド
複数のトランザクションの使い方
4
後続のデータ操作文やトランザクション操作文で、トランザクション名をパラメータと
して使って処理を行います。トランザクション名を指定した文は、そのトランザクショ
ンに制御されながら、関連して機能することになります。
デフォルトのトランザクション
複数のトランザクションを扱うプログラムでは、各トランザクションに対して名前を付け
ておくことが必要です。この場合、その複数のトランザクションの中にデフォルトトランザ
クション(GDS__TRANS)があってもかまいません。デフォルトトランザクションは必ず
しも SET TRANSACTION で起動する必要はありませんが、トランザクションが複数の場
合は、明示的に起動するのが理想的です。また、データ操作文でも名前を使って参照する
ようにします。
データ操作文やトランザクション操作文にトランザクション名が指定されていなかった場
合は、その文では自動的にデフォルトトランザクションが使用されます。また、デフォルト
トランザクションを SET TRANSACTION 文で明示的に起動していない場合は、前処理時
に gpre によりデフォルトトランザクション起動のための文が挿入されます。
重要
DSQL プログラムは、gpre -m スイッチを使って前処理を行わなければなりません。この
スイッチを付けた場合、デフォルトトランザクションの自動生成は行われず、エラーが報
告されます。DSQL では、デフォルトトランザクションを含め、使用するすべてのトラン
ザクションを明示的に起動しておかなければなりません。
カーソルの扱い
DECLARE CURSOR ではトランザクション名を指定するはことはできません。そのかわ
り、トランザクション名は、宣言したカーソルの OPEN 文のパラメータとして指定します。
これで、カーソルとトランザクションが関連付けられます。なお、カーソルに関連付けるこ
とができるトランザクションは 1 つに限られています。たとえば、次は、カーソルを宣言
し、その OPEN 文で T1 というトランザクションを関連付けているコード例です。
. . .
EXEC SQL
DECLARE S CURSOR FOR
SELECT COUNTRY, CUST_NO, SUM(QTY_ORDERED)
FROM SALES
GROUP BY CUST_NO
WHERE COUNTRY = 'Mexico';
EXEC SQL
SET TRANSACTION T1 READ ONLY READ COMMITTED;
. . .
EXEC SQL
OPEN TRANSACTION T1 S;
. . .
なお、OPEN 文にトランザクション名を指定しなかったときは、文はデフォルトトランザ
クション(GDS__TRANS)に関連付けられます。
第 4 章 トランザクションの操作
4-27
複数のトランザクションの使い方
OPEN 文によりトランザクションの関連付けが完了すると、後続のカーソル関連文も、そ
のトランザクションに自動的に関連付けられ、その制御のもとで機能します。このため、
カーソル関連文では、パラメータとしてトランザクション名を指定することはできません。
次は、t2 というトランザクションを OPEN 文で S というカーソルと関連付け、その後、
カーソル S を FETCH と CLOSE で処理しているコード例です。
. . .
EXEC SQL
OPEN TRANSACTION t2 S;
EXEC SQL
FETCH S INTO :country, :cust_no, :qty;
while (!SQLCODE)
{
printf("%s %d %d¥n", country, cust_no, qty);
EXEC SQL
FETCH S INTO :country, :cust_no, :qty;
}
EXEC SQL
CLOSE S;
. . .
1 つのトランザクションで複数のカーソルを扱うこともでき、逆に、複数のトランザクショ
ンで 1 つのカーソルを制御することもできるので、必要に応じた柔軟な対応が可能です。
複数のトランザクションを使用する場合の処理例
複数のトランザクションを扱うプログラムの簡単な C のコード例を以下に示します。ここ
では、mytrans1 と mytrans2 という 2 つのトランザクションハンドルを宣言するとともに、
ハンドルを 0 に初期化した後、この 2 つのトランザクションを起動しています。その後、
後続のデータ操作文でトランザクション名を使って処理を行っています。データ操作文に
よっては、トランザクション名とともにカーソルを記述しているものもあります。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
long *mytrans1 = 0L, *mytrans2 = 0L;
char city[26];
EXEC SQL
END DECLARE SECTION;
. . .
EXEC SQL
DECLARE CITYLIST CURSOR FOR
SELECT CITY FROM CITIES
WHERE COUNTRY = 'Mexico';
EXEC SQL
SET TRANSACTION NAME mytrans1;
EXEC SQL
4-28 埋 め 込 み S Q L ガ イ ド
DSQL での複数のトランザクションの使い方
SET TRANSACTION mytrans2 READ ONLY READ COMMITTED;
. . .
printf("Mexican city to add to database: ");
gets(city);
EXEC SQL
INSERT TRANSACTION mytrans1 INTO CITIES
VALUES :city, 'Mexico', NULL, NULL, NULL, NULL;
EXEC SQL
COMMIT mytrans1;
EXEC SQL
OPEN TRANSACTION mytrans2 CITYLIST;
EXEC SQL
FETCH CITYLIST INTO :city;
while (!SQLCODE)
{
printf("%s¥n", city);
EXEC SQL
FETCH CITYLIST INTO :city;
}
EXEC SQL
CLOSE CITYLIST;
EXEC SQL
COMMIT mytrans2;
EXEC SQL
DISCONNECT
. . .
DSQL での複数のトランザクションの使い方
InterBase では、DSQL で複数のトランザクションを扱えるようになっています。ただし、
この場合、次のような制限があります。
• プログラムは、gpre -m スイッチを使って前処理を行わなければなりません。
• トランザクション名は、静的に宣言しなければなりません。 実行時に、ユーザーが変数を
使って定義することはできません。
• トランザクション名は、事前に 0 に初期化しておいてから DSQL 文で使用しなければなり
ません。
• トランザクションはすべて、SET TRANSACTION 文で明示的に起動しなければなりませ
ん。
• 埋め込みプログラムの場合、データ定義言語(DDL)は名前付きトランザクションの処
理部分では使用できません。DDL は、必ずデフォルトトランザクションである
GDS__TRANS との関連で使用しなければなりません。
第 4 章 トランザクションの操作
4-29
DSQL での複数のトランザクションの使い方
• トランザクション名の記述のない SET TRANSACTION 文を使用して、実行時に、ユー
ザーによるトランザクションの動作(アクセスモードやロック対応など)の変更が可能で
す。この場合、PREPARE の後にトランザクション名を指定するとともに、後続の
EXECUTE 文でトランザクション名を指定することで、動作の変更が実行されます。
PREPARE と EXECUTE のほか、EXECUTE IMMEDIATE 文も使用できます。
InterBase では、どのプログラムでも、トランザクション名は前処理を行う際に固定されま
す。したがって、ユーザーが動的に割り当てることはできません。ただし、実行時、ユー
ザーが DSQL トランザクションの動作(アクセスモードやロック対応など)を変更するこ
とは可能です。なお、ユーザーによる動作の変更が予想される場合、プログラム上で相応の
処理を記述しておかなければなりません。次の節では、ユーザーによるトランザクション動
作の変更方法について説明します。
“?” によるトランザクションの動作の変更
DSQL プログラムでは、使用できるトランザクションの名前と数は、InterBase のプリプロ
セッサである gpre で前処理を行った際に設定されます。したがって、DSQL 文で使用する
名前付きトランザクションは、事前にプログラマが決めておかなければなりません。また、
各トランザクションの動作も事前に決めておかなければなりません。このように、トランザ
クション名はユーザーが変更することはできませんが、トランザクションの動作について
は、実行時にユーザーが変更することができます。
DSQL プログラムでは、ユーザーが SQL 文を入力することができます。この文はホスト言
語の文字列変数として格納され、格納された変数は PREPARE 文または EXECUTE 文で
処理できます。また、この 2 つの文のかわりに、EXECUTE IMMEDIATE を使用するこ
ともできます。
PREPARE
• 変数に代入されている文にエラーがないかどうかを確認します。
• 文を XSQLDA にロードし、後続の EXECUTE 文に渡します。
EXECUTE IMMEDIATE
• 文にエラーがないかどうかを確認します。
• 文を XSQLDA にロードします。
• 文を実行します。
EXECUTE も EXECUTE IMMEDIATE も、すでに SET TRANSACTION で起動されて
いるトランザクションに関連して動作します。この場合、トランザクションは名前付きト
ランザクションでもデフォルトトランザクション(GDS__TRANS を指定)でもかまいま
せん。なお、トランザクション名を指定しなかったときは、EXECUTE または EXECUTE
IMMEDIATE はデフォルトトランザクション(GDS__TRANS)に対して機能します。
EXECUTE 文と EXECUTE IMMEDIATE 文のトランザクション動作を変更するには、次
の 2 つが処理のポイントとなります。
• ユーザーに SET TRANSACTION 文の入力を要求し、入力を変数に格納します。
4-30 埋 め 込 み S Q L ガ イ ド
DSQL での複数のトランザクションの使い方
• SET TRANSACTION 文を使用して、事前に動作を変更するトランザクションを起動して
おきます。このトランザクションの動作が、後続の EXECUTE または EXECUTE
IMMEDIATE で変更されることになります。
起動済みのトランザクション(デフォルトまたは名前付き)の動作は、ユーザー入力の
SET TRANSACTION 文によって変更されることになります。この変更は、次の SET
TRANSACTION 文が実行されると無効になります。
次は、ユーザーにトランザクションの動作を変更するかどうかを尋ね、変更の指示があれ
ば実行に移す C のコードです。また、次のユーザー文では、変更後のトランザクションを
使って処理を行います。その後、トランザクションの動作を復元しています。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
char usertrans[512], query[1024];
char deftrans[] = {"SET TRANSACTION READ WRITE WAIT SNAPSHOT"};
EXEC SQL
END DECLARE SECTION;
. . .
printf("¥nEnter SQL statement: ");
gets(query);
printf("¥nChange transaction behavior (Y/N)? ");
gets(usertrans);
if (usertrans[0] == "Y" || usertrans[0] == "y")
{
printf("¥nEnter ¥"SET TRANSACTION¥" and desired behavior: ");
gets(usertrans);
EXEC SQL
COMMIT usertrans;
EXEC SQL
EXECUTE IMMEDIATE usertrans;
}
else
{
EXEC SQL
EXECUTE IMMEDIATE deftrans;
}
EXEC SQL
EXECUTE IMMEDIATE query;
EXEC SQL
EXECUTE IMMEDIATE deftrans;
. . .
重要
上のコード例でもわかるように、動作中のトランザクションを必ずコミット(またはロー
ルバック)してから、SET TRANSACTION 文(EXECUTE IMMEDIATE)を実行しな
ければなりません。
第 4 章 トランザクションの操作
4-31
4-32 埋 め 込 み S Q L ガ イ ド
第
章
データ定義文
第5章
この章では、SQL アプリケーションでデータベースを作成、修正、削除する方法について
説明します。また、データベースのテーブル、ビュー、インデックスの作成、修正、削除
の方法についても説明しています。データベースは基本的にテーブル、ビュー、インデック
スで構成され、この 3 つの要素による基本構造をメタデータと呼びます。
重要
この章で説明する文は、DSQL(動的 SQL)アプリケーションでも使用できます。なお、
DSQL アプリケーションでは、このような文を実行時にユーザーが記述する場合、先頭に
EXEC SQL を記述する必要はありません。
メタデータの作成、修正、削除には、InterBase の対話型 SQL ツールである isql を使用す
るのが一般的です。ただし、場合によっては、SQL アプリケーションにデータ定義文を記
述しなければならないこともあり、また、この方法が適していることもあります。SQL、お
よび DSQL アプリケーションで、次のようなデータ定義文を利用できます。
表 5.1
埋め込みアプリケーションで使用できるデータ定義文
CREATE 文
ALTER 文
DROP 文
CREATE DATABASE
ALTER DATABASE
—
CREATE DOMAIN
ALTER DOMAIN
DROP DOMAIN
CREATE GENERATOR
SET GENERATOR
—
CREATE INDEX
ALTER INDEX
DROP INDEX
CREATE SHADOW
ALTER SHADOW
DROP SHADOW
CREATE TABLE
ALTER TABLE
DROP TABLE
CREATE VIEW
—
DROP VIEW
DECLARE EXTERNAL
—
DROP EXTERNAL
DECLARE FILTER
—
DROP FILTER
第 5 章 データ定義文
5-1
メタデータの作成
DSQL では、上記の他に、ストアドプロシージャ、トリガー、例外の作成、変更、削除も
行うことができます。また、DSQL は、ユーザーが実行時にデータ定義文を入力できると
いう点で、データ定義機能に関してはたいへん優れています。たとえば、isql はデータ定義
のためのツールですが、この isql も DSQL アプリケーションの 1 つです。isql を使用した
ストアドプロシージャ、トリガー、および例外の定義の詳細は、
『データ定義ガイド』を参
照してください。DSQL プログラミングの詳細は、第 13 章「動的 SQL」を参照してくだ
さい。
重要
アプリケーションで、データ操作文のほか、データ定義文を使った場合は、gpre -m スイッ
チを使って前処理を行わなければなりません。したがって、このようなアプリケーションで
は、事前に SET TRANSACTION を使って明示的にトランザクションを起動しておく必要
があります。
メタデータの作成
SQL のデータ定義文を使用することで、アプリケーション上でデータベースやテーブルの
作成、修正を行うことができます。ただし、データベースなどの作成や修正には isql を使
用するのが一般的で、こういったデータ定義文は、通常、ユーザーが 1 回だけ使って破棄
するようなアプリケーションで使用します。アプリケーションは、もちろん保存しておい
てもかまいません。保存しておくと、将来データベース開発者がアプリケーションを修正
する際、プログラムコードを参考にしてデータベースの構造をチェックすることができま
す。実際、データ定義文を修正するだけであれば、既存のコードをコピーして、必要に応じ
て手直した方が、最初から作成するよりも簡単です。
CREATE 文は、新規のデータベースやドメイン、テーブル、ビュー、インデックスの作成
に使用します。CREATE 文を記述したときは、その後に必ず COMMIT 文を記述しなけれ
ばなりません。これで、後続の各 CREATE 文で、定義済みの関連メタデータを使用できる
ようになります。たとえば、ドメインの定義では、コミットすることにより、そのドメイン
が後続のテーブル定義で参照されるようになります。
メタデータ名
名前の長さ
テーブル、列、ドメインの名前などのメタデータ名には、最長 68 バイト(67 バイトと
NULL 終端)を使用できます。 旧バージョンの InterBase は、32 バイト以下のメタデータ
名しかサポートしません。このため、旧バージョンのクライアントは、32 バイトより長い
名前を持つデータベースオブジェクトにアクセスできません。
クライアントが長いメタデータ名にアクセスできるようにするには、XSQLDA 構造体の
version フィールドを SQLDA_CURRENT_VERSION に設定します。これは ibase.h で定
義されます。このバージョンは、長い名前を解釈するように設定されています。
区切り付き識別子
InterBase ダイアレクト 1 では、オブジェクト名の大文字と小文字が区別されません。ま
た、キーワード、空白、または非 ASCII 文字を含めることはできません。
5-2 埋 め 込 み S Q L ガ イ ド
メタデータの作成
ダイアレクト 3 では、二重引用符で囲まれたオブジェクト名は区切り付き識別子です。区
切り付き識別子は、大文字と小文字が区別され、キーワード、スペース、非 ASCII 文字を
含むことができます。次に例を示します。
SELECT “CodAR” FROM MyTable
これは、次の文とは異なります。
SELECT “CODAR” FROM MyTable
この動作は、区切り付き識別子に関する ANSI SQL のセマンティクスにしたがっていま
す。
データベースの作成
CREATE DATABASE 文は、新しいデータベースを構築し、それに関連するシステムテー
ブルを作成します。これらはデータベースの内部構造を示すテーブルです。ユーザーが作
成したテーブルと同様に、これらのテーブルの多くに含まれるデータを SQL プログラムで
選択できます。
CREATE DATABASE の構文の最も基本的な形式は次のとおりです。
EXEC SQL
CREATE DATABASE '<filespec>';
CREATE DATABASE は、他の CREATE 文より前に記述しなければなりません。パラ
メータは 1 つで、作成するデータベース名を指定します。たとえば、次は、countries.ib と
いう名前のデータベースを作成する文例です。
EXEC SQL
CREATE DATABASE 'countries.ib';
メモ
CREATE DATABASE では、ホスト名やノード名、ディレクトリパスなどのフルファイル
名を指定することもでき、この場合、その場所にデータベースが作成されることになりま
す。なお、ファイル仕様はオペレーティングシステムによって異なります。詳細は、オペ
レーティングシステムのマニュアルを参照してください。
重要
InterBase では、リモートデータベースにアクセスできるため、データベースをリモートマ
シンに作成することもできます。ただし、データベースの格納場所は変更できないので、
データベースを常駐させておくマシンに作成しなければなりません。
オプションパラメータ
CREATE DATABASE にはオプションでパラメータを付加することができます。たとえ
ば、クライアントのアプリケーションから InterBase サーバーに接続してデータベースを作
成する場合で、その接続にユーザー名とパスワードという条件を設ける必要があるとしま
す。このような場合、パラメータとして USER と PASSWORD が用意されています。この
ほか、パラメータを使ってデータベースのページサイズ、マルチファイルデータベースの
数とサイズ、データベースのデフォルトのキャラクタセットなどの設定を行うこともでき
ます。
CREATE DATABEASE のパラメータの詳細は、『データ定義ガイド』を参照してくださ
い。CREATE DATABASE の構文の詳細は、『言語リファレンス』を参照してください。
第 5 章 データ定義文
5-3
メタデータの作成
重要
データベースを作成するアプリケーションは gpre -m スイッチを使って前処理しておかな
ければなりません。加えて、gpre -m スイッチを使ってテーブルを最低 1 つ作成しておく
必要があります。事前にテーブルを作成せずにデータベースを作成した場合、別のプログラ
ムはそのデータベースを正常に開けません。データ定義とデータ操作の両方を処理するア
プリケーションでは、必ず DECLARE TABLE を使ってテーブルを宣言してからテーブル
の作成と母集団の入力を行ってください。テーブルの作成の詳細は、5-5 ページの「テー
ブルの作成」を参照してください。
デフォルトキャラクタセットの指定
CREATE DATABASE では、パラメータを使ってデータベースのデフォルトキャラクタ
セットを指定することができます。ここでキャラクタセットを指定しておいた場合、
CHAR、VARCHAR、テキスト BLOB のデータが、サーバー側で自動的にそのキャラク
タセットにコード変換され格納されます。ただし、明示的にキャラクタセットの指定があっ
たときは、そのキャラクタセットが使用されます。このデフォルトキャラクタセットは、
CREATE DATABASE でデータベースを作成する際は必ず指定しておきます。
デ フ ォ ル ト キ ャ ラ ク タ セ ッ トの指定は、CREATE
DATABSE 文の DEFAULT
CHARACTER SET 句を使用します。たとえば、次の文では、ISO8859_1 キャラクタセッ
トを使用するデータベースが作成されます。
EXEC SQL
CREATE DATABASE 'europe.ib' DEFAULT CHARACTER SET ISO8859_1;
キャラクタセットを指定しなかった場合、キャラクタセットはデフォルトの NONE に設定
されます。デフォルトが NONE であるということは、列で使用されるキャラクタセットが
存在しないことを意味します。したがって、データは、入力の時点で使用しているキャラ
クタセットを使って列に格納され、また取り出されることになります。NONE が設定され
ている列には、後で任意のキャラクタセットをロードすることができますが、異なるキャ
ラクタセットが設定されている列にそのデータを後で移動することはできません。この場
合には、移動元のキャラクタセットと移動先のキャラクタセットとの間でコード変換は行
われず、このため、移動中にエラーが発生することがあります。
DEFAULT CAHRACTER SET 句、および InterBase がサポートするキャラクタセットの
詳細は、『データ定義ガイド』を参照してください。
ドメインの作成
CREATE DOMAIN を使用して、ドメイン(列で使用されるデータ型、名前付き)を作成
することができます。作成したドメインは、データベース全体で有効で、後続の CREATE
TABLE 文で使用することができます。特に、データベースのテーブル数が多く、各テーブ
ルに同じ定義(データ型)の列がある場合に、CREATE DOMAIN を使ってドメインを作
成しておくと便利です。たとえば、従業員管理データベースでは、通常、複数のテーブルに
従業員の姓と名を記録するテーブルが配置されます。このような場合に、各テーブルの作
成にドメインを使用します。
CREATE DOMAIN の基本構文は、次のようになります。
EXEC SQL
CREATE DOMAIN name AS <datatype>;
5-4 埋 め 込 み S Q L ガ イ ド
メタデータの作成
たとえば、FIRSTNAME と LASTNAME という名前で 2 つのドメインを作成する場合、
記述は次のようになります。
EXEC SQL
CREATE DOMAIN FIRSTNAME AS VARCHAR(15);
EXEC SQL
CREATE DOMAIN LASTNAME AS VARCHAR(20);
EXEC SQL
COMMIT;
ドメインの作成とコミットが終了すれば、そのドメインは後続の CREATE TABLE 文の列
の定義で利用できます。たとえば、次の CREATE TABLE 文は、EMPLOYEE というテー
ブルを作成する場合で、ここでは、列の定義でデータ型を指定するかわりに、FIRSTNAME
と LASTNAME という 2 つのドメインを使用しています。
EXEC SQL
CREATE TABLE EMPLOYEE
(
. . .
FIRST_NAME FIRSTNAME NOT NULL,
LAST_NAME LASTNAME NOT NULL;
. . .
);
また、ドメインでは、デフォルト値、NOT NULL 属性、CHECK 制約(挿入や更新で値
が一定範囲に限定されます)、キャラクタセット、照合順序を指定することもできます。
ドメインの作成と、テーブルの作成中にドメインを使用する方法の詳細は、
『データ定義ガ
イド』を参照してください。CREATE DOMAIN の構文の詳細は、
『言語リファレンス』
を参照してください。
テーブルの作成
テーブルの新規作成には、CREATE TABLE 文を使用します。文では、テーブルの名前と
列を指定します。オプションで整合性制約を指定することもできます。列にはそれぞれ、
キャラクタセットや照合順序を付加することができます。CREATE TABLE が実行される
と、そのテーブルに対して自動的にデフォルトの SQL セキュリティスキーマが設定されま
す。また、テーブルの作成者がオーナーとなります。オーナーにはテーブルに関連する特権
がすべて割り当てられます。この特権には、他のユーザーに対して特権を認可する権利も
含まれます。
テーブルを作成するには、事前にデータベースを作成しておかなければなりません。
CREATE TABLE の基本構文は、次のとおりです。
EXEC SQL
CREATE TABLE name (<col_def> | <table_constraint>
[, <col_def> | <table_constraint> ...]);
ここで、col_def は次の構文を使って列を定義します。
<col> {<datatype> | COMPUTED [BY] (<expr>) | domain}
[DEFAULT {literal | NULL | USER}]
第 5 章 データ定義文
5-5
メタデータの作成
[NOT NULL]
[<col_constraint>]
[COLLATE collation]
col は列の名前で、同じテーブル定義内で一意でなければなりません。
datatype はデータ型で、その列に入力される値のデータ型を指定します。COMPUTED BY
は、実行時のアクセスの際に、なんらかの式によってその列の値が与えられる場合に使用
します。
col_constraint は、オプションの整合性制約で、その列全体に対して適用されます。
table_constraint は、同じくオプションの整合性制約で、テーブル全体に対して働きます。
これらの整合性制約は、なんらかの条件に合致するデータだけをテーブルに入力する場合
に利用できます。整合性制約では、このほか、テーブルや列にデータが重複して入力され
ないようにしたり、データベース中の他のテーブルとの参照の整合性の設定などが可能で
す。
列定義には、デフォルト値を含めることができます。例:
stringfld VARCHAR(10) DEFAULT ‘abc’
integerfld INTEGER DEFAULT 1
numfld NUMERIC(15,4) DEFAULT 1.5
datefld1 DATE DEFAULT ‘2/01/2001’
datefld2 DATE DEFAULT ‘TODAY’
userfld VARCHAR(12) DEFAULT USER
最後の 2 行では、InterBase の特別な機能を使用しています。‘TODAY’ は、その時点の日
付になります。USER は、その列の挿入を行っているユーザーです。
次の CREATE DATABASE 文と CREATE TABLE 文の例では、employee.ib というデー
タベースを作成するとともに、EMPLOYEE_PROJECT という名前のテーブルを作成して
います。列は、EMP_NO、PROJ_ID、DUTIES です。
EXEC SQL
CREATE DATABASE 'employee.ib';
EXEC SQL
CREATE TABLE EMPLOYEE_PROJECT
(
EMP_NO SMALLINT NOT NULL,
PROJ_ID CHAR(5) NOT NULL,
DUTIES Blob SUB_TYPE 1 SEGMENT SIZE 240
);
EXEC SQL
COMMIT;
複数のテーブルを作成することができますが、名前の重複するものは作成できません。
SQL データ型と整合性制約の詳細は、
『データ定義ガイド』を参照してください。CREATE
TABLE 構文の詳細は、『言語リファレンス』を参照してください。テーブル特権の変更ま
たは割り当ての詳細は、
『データ定義ガイド』の「セキュリティの計画」を参照してください。
5-6 埋 め 込 み S Q L ガ イ ド
メタデータの作成
計算列の作成
計算列とは、実行時、その列にアクセスが行われると同時に値が計算されるような列を言
います。値は、正規の SQL の式により与えられます。この場合、式は、配列ではない 1 つ
の値を出力するものでなければなりません。計算列は、データベースに物理的に格納されて
いるデータに対応していないという意味で「仮想的」であると言えます。計算列の値は常に
SELECT クエリーが行われるときに生成されます。基になっている値が変更された場合
は、それに対応して動的に生成されます。
計算列を作成する場合、CREATE TABLE 文で次の列宣言構文を記述します。
col COMPUTED [BY] (<expr>)
expr は式です。式では、テーブルに定義されている列の値を使用できます。たとえば、次
の文は、FIRST_NAME と LAST_NAME という 2 つの列の値を連結して、FULL_NAME
という計算列に入力する例です。
EXEC SQL
CREATE TABLE EMPLOYEE
(
. . .
FIRST_NAME VARCHAR(10) NOT NULL,
LAST_NAME VARCHAR(15) NOT NULL,
. . .
FULL_NAME COMPUTED BY (LAST_NAME || ', ' || FIRST_NAME)
);
COMPUTED BY の詳細は、『データ定義ガイド』を参照してください。
テーブルの宣言と作成の関係
CREATE TABLE 文を使ってテーブルを作成する場合、それに先だって、DECLARE
TABLE でテーブルの宣言を行っておく必要があります。この宣言により、テーブルの構
造が InterBase のプリプロセッサである gpre に報告されることになります。つまり、gpre
での前処理中に DECLARE TABLE 文が見つかると、宣言されているテーブルの内容が
いったん記憶されます。CREATE TABLE 文が見つかった場合、その CREATE 文での列
定義と、記憶されている DECLARE 文の列定義が一致しているかどうかの検査が行われま
す。この検査で列定義が一致しないときは、gpre によりエラーが報告され、前処理が中止
されます。この場合、プログラムを修正しなければなりません。
DECLARE TABLE 文は CREATE TABLE 文の前に記述しなければなりません。
たとえば、次の例では、DECLARE TABLE で EMPLOYEE_PROJECT というテーブル
を宣言した後に、CREATE TABLE でテーブルを作成しています。
EXEC SQL
DECLARE EMPLOYEE_PROJECT TABLE
(
EMP_NO SMALLINT,
PROJ_ID CHAR(5),
DUTIES Blob(240, 1)
);
EXEC SQL
第 5 章 データ定義文
5-7
メタデータの作成
CREATE TABLE EMPLOYEE_PROJECT
(
EMP_NO SMALLINT,
PROJ_ID CHAR(5),
DUTIES Blob(240, 1)
);
EXEC SQL
COMMIT;
DECLARE TABLE の詳細は、『言語リファレンス』を参照してください。
ビューの作成
ビューとは、データベース中の 1 つ以上のテーブルの一部をもとに作成された仮想テーブ
ルです。ビューの使用には、次のような利点があります。
• テーブルに公開したくないデータがある場合、そのデータを除外したビューを作成するこ
とで、このようなデータに対するユーザーのアクセスを制限できます。
• 複数のテーブルがある場合、各テーブルから必要なデータだけを選択し、再編成したテー
ブル(ビュー)を作成できます。このビューのデータは、ユーザーからの読み取りが可能
です。
テーブルのデータはデータベースに格納されますが、ビューの場合、これとは異なり、作成
の際にその定義だけがデータベースに格納されます。プログラムでビューが必要になった
場合は、InterBase によりビューの定義が読み取られるとともに、テーブルと同じように瞬
時に出力が生成されます。
ビューは、次の CREATE VIEW 構文を使って作成できます。
EXEC SQL
CREATE VIEW name [(view_col [, view_col ...)]AS
<select> [WITH CHECK OPTION];
上記の構文中、name はビュー名です。この名前は、そのデータベースに同じものがあって
はなりません。
ビューの列に独自の名前(view_col)、つまり作成元のテーブルの列とは違う名前を付けた
いときは、その名前を name の右にカンマで区切って順に記述します。先頭と末尾はかっ
こで囲みます。これで、その名前が、CREATE VIEW の SELECT 文の右に指定した作成
元のテーブルの列の順番どおりに、ビューの列にそれぞれ割り当てられることになります。
なお、独自の名前を記述しなかったときは、テーブルの列名がビューでもそのまま使用さ
れます。
ビューに独自の名前を付けた場合、その名前はテーブルの列名とは独立して扱われます。こ
のため、作成元のテーブルの構成が変更されても、ビューの構成は変わりません。
メモ
ビューの列名は、CREATE VIEW の SELECT 文の右に指定した列名と同じ数だけ記述す
るか、または、何も記述しないかのどちらかでなければなりません。
5-8 埋 め 込 み S Q L ガ イ ド
メタデータの作成
select 句の部分には、通常の SELECT 文を記述します。つまり、ビューに取り込む列名の
他 に、ビ ュ ー に 取 り 込 む 行の選択条件を指定できます。ただし、CREATE VIEW の
SELECT では、ORDER BY 句は使用できません。また、DSQL では、ORDER BY 句の
他に、UNION 句も使用できません。
WITH CHECK OPTION はオプションで、このオプションを使用することにより、ビュー
に対するデータの挿入、更新、削除が制限されます。
ビューには、読み取り専用のビューと更新可能なビューの 2 種類があります。読み取り専
用のビューを作成する場合、ビューの作成者がビューの作成元の 1 つ以上のテーブルに対
し SELECT 特権を所有していることが必要です。一方、更新可能なビューを作成する場合、
ビューの作成元の 1 つ以上のテーブルに対し ALL 特権を持っていることが条件となりま
『データ定義ガイド』の「セキュリティの計画」を参照してください。
す。SQL 特権の詳細は、
読み取り専用のビューの作成
ビューは、複数のテーブルに基づいて作成することができます。また、既存の複数のビュー
を基礎にしてビューを作成することもできます。このように、複数のテーブルまたは他の
ビューをもとにして作成したビューは、自動的に読み取り専用のビューとなります。した
がって、更新は不可能です。たとえば、次は、読み取り専用のビューを作成する文の例で
す。作成するビュー名は PHONE_LIST で、EMPLOYEE と DEPARTMENT の 2 つが作
成元のテーブルです。したがって、このビューは読み取り専用となります。
EXEC SQL
CREATE VIEW PHONE_LIST AS
SELECT EMP_NO, FIRST_NAME, LAST_NAME, LOCATION, PHONE_NO
FROM EMPLOYEE, DEPARTMENT
WHERE EMPLOYEE.DEPT_NO = DEPARTMENT.DEPT_NO;
EXEC SQL
COMMIT;
重要
作成したビューは、そのままの状態では作成者しかアクセスすることができません。一般の
ユーザーは、GRANT で許可を与えられた後で読み取りが可能になります。GRANT の詳
細は、『データ定義ガイド』の「セキュリティの計画」を参照してください。
更新可能なビューの作成
更新可能なビューとは、更新の特権が与えられているユーザーが、ビューの作成元のテー
ブルのデータに対して、挿入、更新、削除といった処理が可能なビューです。なお、更新可
能なビューを作成する場合、次のような条件に適合していなければなりません。
• 作成元のテーブルが 1 つであること。既存のビューが作成元でもかまいませんが、この場
合、そのビューが 1 つで更新可能であることが必要です。
• ビューの SELECT 文で指定されているテーブルは 1 つでなければなりません。
• 作成元のテーブルに計算列がある場合、その列がビューに取り込まれていてはなりません。
• ビューの SELECT 文には、次の句などが記述されていてはなりません。
• DISTINCT が記述されている WHERE 句
• HAVING 句
• 関数
第 5 章 データ定義文
5-9
メタデータの作成
• ネストされたクエリー
• ストアドプロシージャ
次は、HIGH_CITIES という名前の更新可能ビューを作成する場合の記述です。この例で
は、WHERE で指定されている条件により、CITIES というテーブル中の ALTITUDE が
2640 フィート以上の市(行)がすべてビューに取り込まれます。
EXEC SQL
CREATE VIEW HIGH_CITIES AS
SELECT CITY, COUNTRY_NAME, ALTITUDE FROM CITIES
WHERE ALTITUDE >= 2640;
EXEC SQL
COMMIT;
上のビューの場合、このビューに対して INSERT 特権や UPDATE 特権を所有するユー
ザーであれば、作成元のテーブルである CITIES に行を挿入したり、新規の行を追加する
ことができます。このほか、ビュー HIGH_CITIES に取り込まれていない行に対しても、
挿入や更新が可能です。たとえば、次は、INSERT を使って CITIES テーブルに、市名が
Santa Cruz、国名が United States、高度が 23 フィートというレコード(行)を追加する場
合の記述です。
EXEC SQL
INSERT INTO HIGH_CITIES (CITY, COUNTRY_NAME, ALTITUDE)
VALUES ('Santa Cruz', 'United States', '23');
ビューに取り入れた行以外は、ユーザーに挿入や更新を制限する場合は、WITH CHECK
OPTION を付加します。たとえば、次の文は、ビュー HIGH_CITIES を WITH CHECK
OPTION を使って作成する場合の記述です。これで、INSERT 特権や UPDATE 特権を所
有するユーザーであっても、市の高度が 2640 フィート以上という条件に該当しない行は入
力できなくなります。
EXEC SQL
CREATE VIEW HIGH_CITIES AS
SELECT CITY, COUNTRY_NAME, ALTITUDE FROM CITIES
WHERE ALTITUDE > 2640 WITH CHECK OPTION;
インデックスの作成
SQL には CREATE INDEX が用意されており、この文を使ってユーザー定義のインデッ
クスを作成できます。インデックスは、テーブル中の列をもとにして作成される参照テーブ
ルで、CREATE INDEX では列を指定して作成できます。列に対してインデックスを作成
しておくと、クエリーの際にデータの検索が高速化されます。CREATE INDEX の構文は
次のとおりです。
EXEC SQL
CREATE [UNIQUE] [ASC[ENDING] | DESC[ENDING]] INDEX <index> ON
table (col [, col ...]);
たとえば、次の文では、EMPLOYEE テーブルの LAST_NAME と FIRST_NAME という
2 つの列に対して、NAMEX という名前のインデックスを作成しています。
EXEC SQL
5-10 埋 め 込 み S Q L ガ イ ド
メタデータの作成
CREATE INDEX NAMEX ON EMPLOYEE (LAST_NAME, FIRST_NAME);
メモ
InterBase では、テーブルの定義の際、UNIQUE や PRIMARY KEY などの制約を指定し
て お く と、自 動 的 に シ ス テムレベルのインデックスが生成されます。UNIQUE と
PRIMARY KEY の詳細は、『データ定義ガイド』を参照してください。
CREATE INDEX 構文の詳細は、『言語リファレンス』を参照してください。
インデックスによる重複データの入力の排除
場合によっては、列に同じデータを重複して入力してしまうことがありますが、これは、
CREATE INDEX に UNIQUE キーワードを付加することにより防止できます。たとえば、
次の文は、PROJECT テーブルに、PRODTYPEX という名前のユニークインデックスを作
成している例です。
EXEC SQL
CREATE UNIQUE INDEX PRODTYPEX ON PROJECT (PRODUCT, PROJ_NAME);
重要
ユニークインデックスの作成後は、ユーザーは、その列にすでに記録されているものと重
複するデータを入力することができなくなります。また、更新後のデータと同じデータが
すでに記録されている場合も同じです。なお、上記の PRODTYPEX のように、複数の列
に対してユニークインデックスが定義されている場合、いずれか 1 つの列には重複して
データを入力できますが、インデックスが設定されている列全部にデータを入力したとき、
その組み合わせと同じものが他の行にあった場合、入力は拒否されます。
インデックスのソート順序の指定
SQL では、デフォルトではインデックスは昇順で作成されます。昇順ではなく、降順でイ
ンデックスを作成するときは、DESCENDING キーワードを使ってインデックスを定義し
ます。たとえば、SALARY_HISTORY テーブルの CHANGE_DATE という列をもとに
CHANGEX というインデックスを作成する場合、次のようになります。
EXEC SQL
CREATE DESCENDING INDEX CHANGEX ON SALARY_HISTORY (CHANGE_DATE);
メモ
降順のインデックスが設定されている列からデータを取り出すときは、SELECT
ORDER BY を使って降順を指定します。
文で
ジェネレータの作成
ジェネレータとは、単調に増減する数値を生成するメカニズムを指します。このジェネレー
タで生成される数値は、アプリケーションの SQL 文またはトリガーを使って項目に挿入で
きます。ジェネレータは、通常、列に一意な値を挿入する場合に作成しておきます。ジェネ
レータにより数値が入力された列は、主キー(ソートでの基本列)として利用できます。
アプリケーションで使用するジェネレータは、CREATE GENERATOR 構文を使って作成
できます。構文は次のとおりです。
EXEC SQL
CREATE GENERATOR name;
たとえば、従業員にそれぞれ一意な番号を割り当てたい場合、次の文でジェネレータを作
成しておきます。名前は、内容がわかりやすいように EMP_NO_GEN としておきます。
第 5 章 データ定義文
5-11
メタデータの削除
EXEC SQL
CREATE GENERATOR EMP_NO_GEN;
EXEC SQL
COMMIT;
ジェネレータによって生成される数値の初期値は、SET GENERATOR で指定することが
できます。また、数値は、割り当て文で InterBase ライブラリの GEN_ID() 関数を使って
項目に挿入できます。GEN_ID()、CREATE GENERATOR、および SET GENERATOR
の詳細は、『データ定義ガイド』を参照してください。
メタデータの削除
作成したメタデータ(テーブル、ビュー、インデックス)は、次の SQL 文を使って削除で
きます。
• DROP TABLE:データベースからテーブルを削除するために使用します。
• DROP VIEW:データベースからビューの定義を削除するために使用します。
• DROP INDEX:インデックスを削除するために使用します。
• ALTER TABLE:テーブルの列を削除するために使用します。
ALTER TABLE を含んだ列の削除の詳細は、5-14 ページの「テーブルの修正」を参照し
てください。
インデックスの削除
インデックスは、DROP INDEX を使って削除できます。インデックスを削除できるのは、
作成者、SYSDBA またはルート特権を持つユーザーに限られます。また、なんらかの処理
でインデックスが使用されている最中に削除が行われたときは、インデックスの使用が終
了するのを待って削除が行われます。DROP INDEX の構文は次のとおりです。
EXEC SQL
DROP INDEX name;
上記の構文中、name は削除するインデックス名です。たとえば、NEEDX という名前のイ
ンデックスを削除する場合、次のようになります。
EXEC SQL
DROP INDEX NEEDX;
EXEC SQL
COMMIT;
なお、インデックスに UNIQUE、PRIMARY KEY、FOREIGN KEY などの整合性制約が
設定されている場合、そのままでは削除できません。このようなインデックスを削除するに
は、まず、その制約を削除します。または、制約が設定されている列またはテーブルを削
除します。
DROP INDEX と整合性制約の削除の詳細は、『データ定義ガイド』を参照してください。
5-12 埋 め 込 み S Q L ガ イ ド
メタデータの削除
ビューの削除
ビ ュー は、DROP VIEW を使って削除できます。ビューを削除できるのは、作成者、
SYSDBA またはルート特権を持つユーザーに限られます。また、なんらかの処理でビュー
が使用されている最中に削除が行われたときは、ビューの使用が終了するのを待って削除
が行われます。DROP VIEW の構文は次のとおりです。
EXEC SQL
DROP VIEW name;
EMPLOYEE_ SALARY という名前のビューを削除する場合、次のようになります。
EXEC SQL
DROP VIEW EMPLOYEE_SALARY;
EXEC SQL
COMMIT;
なお、削除するビューが別のビューで使用されている場合は、削除はできません。トリガー
や計算列で使用されている場合も同じです。このようなビューは、次の手順で削除できます。
1
2
そのビューを使用しているビュー、トリガー、計算列を削除します。
この後、ビューを削除します。
DROP VIEW の詳細は、『データ定義ガイド』を参照してください。
テーブルの削除
データベースのテーブルは、DROP TABLE を使って削除できます。テーブルを削除でき
るのは、作成者、SYSDBA またはルート特権を持つユーザーに限られます。なんらかの処
理でテーブルが使用されている最中に削除が行われたときは、テーブルの使用が終了する
のを待って削除が行われます。DROP TABLE の構文は次のとおりです。
EXEC SQL
DROP TABLE name;
ここで、name は削除するテーブル名です。たとえば、EMPLOYEE という名前のテーブル
を削除する場合、次のようになります。
EXEC SQL
DROP TABLE EMPLOYEE;
EXEC SQL
COMMIT;
なお、削除するテーブルがビューで使用されている場合、削除はできません。トリガーや
計算列で使用されている場合も同じです。また、テーブルに整合性制約である UNIQUE や
PRIMARY KEY が設定されており、このような制約が他のテーブルの FOREIGN KEY に
よって参照されている場合も削除できません。このような場合、まず、他のテーブルの
FOREIGN KEY を削除した後、テーブルを削除します。
メモ
テーブル中の列は、全部一括して削除する必要はなく、任意の列を指定して削除できます。
詳細は、5-15 ページの「既存の列の削除」を参照してください。
DROP TABLE の詳細は、『データ定義ガイド』を参照してください。
第 5 章 データ定義文
5-13
メタデータの変更
メタデータの変更
データ定義(データの入力を可能にするための準備)では、通常はテーブルに関する変更
作業が最も多くなります。中でも、テーブルに新規の列を追加したり、不要になった列を
削除する作業が中心になります。SQL では ALTER TABLE が用意されており、この文を
使用して、テーブルへの新規の列の追加や既存の列の削除が可能です。ALTER TABLE で
は、1 つの文で列の追加と削除を同時に行うこともできます。ALTER TABLE などでメタ
データを直接操作すると、メタデータのバージョンがインクリメントされます。データベー
スのバックアップと復元を行うまでの間に、最大で 255 回までテーブル(およびそのトリ
ガー)を変更することができます。
ビューとインデックスを変更する場合は、次の 2 段階の操作が必要です。
1
2
まず、既存のビューやインデックスを削除します。
その後、新規のビューやインデックスを作成します。
場合によっては、既存のメタデータ(テーブル、ビュー、インデックス)を削除できない
ことがあります。このような場合、同じ名前のメタデータは作成できません。メタデータの
削除に失敗する状況としては、次の 3 つがあります。
• メタデータの作成者以外のユーザーが、メタデータを削除しようとした場合
• メタデータに SQL の整合性制約が設定されており、その制約が別のメタデータによって参
照されている場合
• メタデータが、別のビュー、トリガー、計算列で使用されている場合
メタデータの削除の詳細は、5-12 ページの「メタデータの削除」を参照してください。
テーブルの修正
ALTER TABLE では、既存のテーブルに対して次のような修正作業が可能です。
• 列の定義の追加
• テーブルの制約の追加
• 列の定義の削除
• テーブルの制約の削除
• 列に設定されている定義の変更(既存の定義の削除後に別の定義を追加)
• テーブルに設定されている制約の変更(既存の定義の削除後に別の定義を追加)
• 列の名前とデータ型の変更
ALTER TABLE の基本構文は次のとおりです。
EXEC SQL
ALTER TABLE name {ADD colname <datatype> [NOT NULL]
| ALTER [COLUMN] simple_column_name alter_rel_field
| DROP colname | ADD CONSTRAINT constraintname tableconstraint
| DROP CONSTRAINT constraintname};
メモ
テーブルの制約の追加、削除、修正の詳細は、『データ定義ガイド』を参照してください。
5-14 埋 め 込 み S Q L ガ イ ド
メタデータの変更
ALTER TABLE の構文の詳細は、『言語リファレンス』を参照してください。
テーブルへの新規の列の追加
ALTER TABLE を使用して、既存のテーブルに対して新規の列を追加することができま
す。ただし、列の追加が可能なのは、そのテーブルの作成者に限られます。新規の列を追加
する場合、ALTER TABLE の構文は次のようになります。
EXEC SQL
ALTER TABLE name ADD colname <datatype> colconstraint
[, ADD colname datatype colconstraint ...];
たとえば、EMPLOYEE というテーブルに EMP_NO という列を追加する場合、次のよう
になります。
EXEC SQL
ALTER TABLE EMPLOYEE ADD EMP_NO EMPNO NOT NULL;
EXEC SQL
COMMIT;
上記の例では、ドメインである EMPNO を使って列の定義を行っています。ドメインの詳
細は、『データ定義ガイド』を参照してください。
上記の例では追加する列は 1 つですが、複数の列を一度に追加することもできます。この
場合、追加する列名をカンマで区切ります。たとえば、次の文は、EMPLOYEE テーブル
に、EMP_NO と FULL_NAME という 2 つの列を追加する例です。 このうち、
FULL_NAME は計算列で、他の列の値をもとにした計算によって値が得られます。
EXEC SQL
ALTER TABLE EMPLOYEE
ADD EMP_NO EMPNO NOT NULL,
ADD FULL_NAME COMPUTED BY (LAST_NAME || ', ' || FIRST_NAME);
EXEC SQL
COMMIT;
上記の例では、FULL_NAME 列の値は、定義済みの LAST_NAME と FIRST_NAME と
『データ定義ガ
いう 2 つの列によって算出されることになります。計算列の作成の詳細は、
イド』を参照してください。
テーブルに新規の列を追加する場合、整合性制約とともに定義することができます。整合性
制約を持った列をテーブルへ追加する方法の詳細は、
『データ定義ガイド』を参照してくだ
さい。
既存の列の削除
ALTER TABLE では、既存の列の削除を行うこともできます。なお、列を削除した場合、
その列のデータも削除されます。列を削除できるのは、テーブルの作成者、SYSDBA また
はルート特権を持つユーザーに限られます。また、なんらかの処理でテーブルが使用されて
いる最中に削除が行われたときは、テーブルの使用が終了するのを待って削除が行われま
す。ALTER TABLE で列を削除する場合の構文は次のとおりです。
EXEC SQL
ALTER TABLE name DROP colname [, colname ...];
たとえば、次の文では、EMP_NO 列が EMPLOYEE テーブルから削除されます。
第 5 章 データ定義文
5-15
メタデータの変更
EXEC SQL
ALTER TABLE EMPLOYEE DROP EMP_NO;
EXEC SQL
COMMIT;
ALTER TABLE で は、また、複数の列を同時に削除することもできます。たとえば、
EMPLOYEE テーブルから、EMP_NO と FULL_NAME という 2 つの列を削除する場合、
次の文のようになります。
EXEC SQL
ALTER TABLE EMPLOYEE
DROP EMP_NO,
DROP FULL_NAME;
EXEC SQL
COMMIT;
なお、列に UNIQUE、PRIMARY KEY、FOREIGN KEY などの整合性制約が設定されて
いる場合、そのままでは削除できません。この場合、その制約を削除した後、列を削除しま
す。
また、列が、他の列との関連で CHECK 制約により使用された場合も削除はできません。
この場合、先に CHECK 制約を削除してから列を削除します。
整合性制約の詳細は、『データ定義ガイド』を参照してください。
列の変更
ALTER TABLE では、データ型やサイズなど、列の定義の内容を変更することもきます。
すでに列に格納されているデータは、列の内容を変更した時点で失われます。
したがって、格納されているデータが必要な場合、次の 5 段階の手順にしたがって列の定
義の内容を変更しなければなりません。
1
テーブルに一時的な列を追加します。この列に、変更する列(古い列)の現在のメタ
データを一時的に保存することになります。
2
3
4
5
古い列のデータを、新たに作成した一時的な列にコピーします。
古い列の定義を変更します。
一時的な列から古い列へデータをコピーします。
一時的な列を削除します。
例
たとえば、EMPLOYEE テーブルに OFFICE_NO という列があり、その列に定義されてい
るデータ型が CHAR(3) だったとします。この場合、データ型はこのままで、サイズを 1
文字だけ増やすことにしたとします。この場合、サイズの変更は、次の 5 段階の手順にし
たがって進められます。
6
まず、変更プロセス中に OFFICE_NO のデータを保存する一時的な列を作成します。
EXEC SQL
ALTER TABLE EMPLOYEE ADD TEMP_NO CHAR(3);
EXEC SQL
5-16 埋 め 込 み S Q L ガ イ ド
メタデータの変更
COMMIT;
7
既存のデータを OFFICE_NO から TEMP_NO へ移し、保存します。
EXEC SQL
UPDATE EMPLOYEE
SET TEMP_NO = OFFICE_NO;
8 OFFICE_NO の列定義を変更し、データ型と新しいサイズを指定します。
EXEC SQL
ALTER TABLE EMPLOYEE ALTER OFFICE_NO TYPE CHAR(4);
EXEC SQL
COMMIT;
9
データを TEMP_NO から OFFICE_NO へ移します。
EXEC SQL
UPDATE EMPLOYEE
SET OFFICE_NO = TEMP_NO;
10 最後に、TEMP_NO 列を削除します。
EXEC SQL
ALTER TABLE DROP TEMP_NO;
EXEC SQL
COMMIT;
列の定義の削除の詳細は、5-15 ページの「既存の列の削除」を参照してください。
列の定義を追加する方法については、5-16 ページの「列の変更」を参照してください。
ALTER TABLE ALTER コマンドを使用すると、列の位置と名前を変更できます。
たとえば、次の文では、EMPLOYEE テーブルの EMP_NO 列が 3 番めの位置から 2 番め
の位置に移動されます。
ALTER TABLE EMPLOYEE ALTER EMP_NO POSITION 2;
また、次に示すように、EMP_NO 列の名前を EMP_NUM に変更することもできます。
ALTER TABLE EMPLOYEE ALTER EMP_NO TO EMP_NUM;
重要
項目の定義を変更した場合は、インデックスを再構築する必要があります。
ALTER TABLE の構文の詳細は、『言語リファレンス』を参照してください。
ビューの修正
ビューを修正する場合、次の操作が必要になります。
1
2
まず、現在のビューを削除します。
その後、新規のビューを作成します。この場合、名前は削除したビューと同じものとし
ます。
たとえば、次のビューは、従業員の給料に関する情報を読み取るためのビューです。
EXEC SQL
CREATE VIEW EMPLOYEE_SALARY AS
第 5 章 データ定義文
5-17
メタデータの変更
SELECT EMP_NO, LAST_NAME, CURRENCY, SALARY
FROM EMPLOYEE, COUNTRY
WHERE EMPLOYEE.COUNTRY_CODE = COUNTRY.CODE;
このビューでは、従業員の姓しか表示されませんが、姓と名の両方を表示する必要がある
とします。この場合、まず現在のビューを削除します。
EXEC SQL
DROP EMPLOYEE_SALARY;
EXEC SQL
COMMIT;
削除が終わったら、次のようにして新規のビューを作成します。これで、従業員名が表示
されるようになります。
EXEC SQL
CREATE VIEW EMPLOYEE_SALARY AS
SELECT EMP_NO, FULL_NAME, CURRENCY, SALARY
FROM EMPLOYEE, COUNTRY
WHERE EMPLOYEE.COUNTRY_CODE = COUNTRY.CODE;
EXEC SQL
COMMIT;
インデックスの修正
インデックスを修正する場合、次の操作が必要になります。
1 ALTER INDEX を使用して、現在のインデックスを使用停止にします。
2 この後、現在のインデックスを削除します。
3 最後に、新規のインデックスを作成します。新規のインデックス名は、削除したイン
デックスと同じものにします。
インデックスの作成後、それまでとは異なる列にインデックスを設定したり、設定済みの
インデックスをはずす場合があります。また、UNIQUE を使って重複データの入力を防止
したり、逆に許可したり、さらに、ソート順を変更する場合もあります。インデックスの
修正は、通常、このような場合に行います。たとえば、次のように NAMEX という名前の
インデックスがあるとします。
EXEC SQL
CREATE INDEX NAMEX ON EMPLOYEE (LAST_NAME, FIRST_NAME);
その後、重複データの入力を防ぐ必要が出てきました。したがって、UNIQUE キーワード
を追加しなければなりません。この場合、まず、次のようにして現在のインデックスを使用
停止にし、それから削除します。
EXEC SQL
ALTER INDEX NAMEX INACTIVE;
EXEC SQL
DROP INDEX NAMEX;
EXEC SQL
COMMIT;
5-18 埋 め 込 み S Q L ガ イ ド
メタデータの変更
続いて、新規のインデックスを作成します。定義内容は以前と同じで、名前も同じく
NANEX とします。ただし、UNIQUE キーワードを追加します。
EXEC SQL
CREATE UNIQUE INDEX NAMEX ON EMPLOYEE (LAST_NAME, FIRST_NAME);
EXEC SQL
COMMIT
ALTER INDEX を直接使用すると、インデックスのソート順を変更したり、固有入力デー
タまたは重複入力データを取り扱えるようになります。たとえば、NAMEX インデックス
を変更して重複入力データを取り扱えるようにする文には以下のものがあります。
EXEC SQL
ALTER INDEX NAMEX DUPLICATE;
重要
インデックスを直接変更する場合には注意が必要です。たとえば、インデックスの停止およ
び再作成を事前に完了せずに、重複入力データをサポートするインデックスを、固有入力
データが必要なインデックスに変更した場合は、インデックスのパフォーマンスが低下し
ます。
インデックスの削除の詳細は、5-12 ページの「インデックスの削除」を参照してください。
インデックスの作成の詳細は、5-10 ページの「インデックスの作成」を参照してください。
第 5 章 データ定義文
5-19
5-20 埋 め 込 み S Q L ガ イ ド
第
章
データ処理
第6章
埋め込みプログラムの SQL 文は、そのほとんどがデータの読み取りや修正、データベース
への新規データの追加といった処理にかかわるものです。この章では、InterBase で認識可
能なデータ型の紹介と併せて、SQL 式と、次に示す SQL 文を使ったデータの取り出し、修
正、追加、削除を行う方法について説明します。
SELECT 文:データベースに対してクエリーを実行し。つまり、データベースからの既存
のデータの読み取り、取り出しを行うときに使用します。SELECT 文では、必要に応じて、
次の 4 種類の方法でデータを取り出すことができます。
• 1 つのテーブルの 1 行、または 1 行の中の任意のデータの取り出し。この取り出しを
単一行 SELECT と呼びます。
• 1 つのテーブルの複数行、または複数行の中の任意のデータの取り出し。この取り
出しには、DECLARE CURSOR 文の中で SELECT を使用します。
• 複数のテーブルに相互に関係している行がある場合、その行、または関係している
行の中の任意のデータの取り出し。この場合、取り出された行は、仮想テーブルま
たは結果テーブルに格納されます。この取り出しを結合(join)と呼びます。
• 複数のテーブルの全部の行、または全部の行の中の任意のデータの取り出し。この
場合、データは仮想テーブルに格納されます。この取り出しを合併(union)と呼び
ます。
• INSERT 文:テーブルへ新規の行を書き込みます。
• UPDATE 文:テーブルの既存の行を修正します。
• DELETE 文:テーブルの既存の行を削除します。
SELECT 文を使ってデータを取り出す方法については、6-19 ページの「SELECT による
データの取り出し」を参照してください。SELECT を使っての単一行の取り出しの詳細は、
6-35 ページの「1 行の取り出し」を参照してください。複数行の取り出しの詳細は、6-36
ページの「複数行の取り出し」を参照してください。
第 6 章 データ処理
6-1
サポートするデータ型
INSERT を使ってテーブルに新規データを書き込む方法の詳細は、6-55 ページの「データ
の挿入」を参照してください。UPDATE を使ってデータを修正するには、6-61 ページの
「データの更新」を参照してください。DELETE を使ってテーブルからデータを削除するに
は、6-67 ページの「データの削除」を参照してください。
サポートするデータ型
表に対して書き込みまたはクエリーするには、表の構造(含まれる列の種類、列に対して
定義されたデータ型の種類)を把握している必要があります。InterBase では次の表で説明
するとおり、13 種類の基本データ型をサポートします。
表 6.1
InterBase でサポートするデータ型
名前
サイズ
範囲と精度
説明
BLOB
可変
• なし
• BLOB セグメント サイズは 64 KB
• グラフィック、テキスト、デジタ
に制限されます。
ル化した音声などのデータを格納
する場合に使用する動的にサイズ
を変更できるデータ型。
• 基本構造単位はセグメントです。
• BLOB のサブタイプでは BLOB の
内容を示します。
BOOLEAN
CHAR(n)
16 ビット
n 文字
• TRUE
• FALSE
• UNKNOWN
• 有効な値 は、TRUE、FALSE、お
よび UNKNOWN を返します。
• ODS 11 以降、任意の dialect が必
• 1 ~ 32,767 バイト
• キャラクタ セットの文字サイズによ
り 32 KB に収まる最大文字数が決ま
• 固定長の CHAR またはテキスト文
要です。
字列型
• 代替キーワード:CHARACTER
ります。
DATE
DECIMAL (precision,
scale)
32 ビット
可変
(16、32、
64 ビット)
A.D.100 年 1 月 1 日~ A.D.32768 年 2 ISC_DATE
月 29 日
• precision = 0 ~ 18、格納する精度の
最小桁数 precision を指定します。
• scale = 0 ~ {value}、格納する小数
点以下の桁数を指定します。
• 小数点以下 scale 桁の数値。例:
DECIMAL(10, 3) の場合には、数値
のフォーマットは ppppppp.sss に
なります。
• Scale は precision 以下である必要
があります。
64 ビットa
2.225 x 10-308 ~ 1.797 x 10308
IEEE 倍精度:15 桁
FLOAT
32 ビット
1.175 x 10-38 ~ 3.402 x 1038
IEEE 単精度:7 桁
INTEGER
32 ビット
-2,147,483,648 ~ 2,147,483,647
符号付き long(ロングワード)
DOUBLE PRECISION
6-2 埋 め 込 み S Q L ガ イ ド
サポートするデータ型
表 6.1
InterBase でサポートするデータ型 ( 続き )
名前
NUMERIC (precision,
scale)
サイズ
範囲と精度
説明
可変
• precision = 0 ~ 18、格納する正確な
精度の桁数 precision を指定します。
• scale = 0 ~ {value}、格納する小数
• 小数点以下 scale 桁の数値。例:
NUMERIC(10, 3) の場合には、数
値のフォーマットは ppppppp.sss
(16、32、
64 ビット)
点以下の桁数を指定します。
になります。
• Scale は precision 以下である必要
があります。
SMALLINT
16 ビット
-32,768 ~ 32,767
符号付き short(ワード)
TIME
32 ビット
00:00:00.0000 ~ 23:59:59.9999
ISC_TIME
TIMESTAMP
64 ビット
A.D.100 年 1 月 1 日~ A.D.32768 年 2 ISC_TIMESTAMP; には、日付と時間
月 29 日
情報の両方が含まれます。
VARCHAR (n)
n 文字
• 1 ~ 32,765 バイト
• キャラクタ セットの文字サイズによ
り 32 KB に収まる最大文字数が決ま
ります。
• 可変長の CHAR またはテキスト文
字列型
• 代替キーワード:CHAR
VARYING、CHARACTER
VARYING
a. DOUBLE の実際のサイズはプラットフォームで変わります。多くのプラットフォームでは 64 ビット サイズをサ
ポートします。
BLOB データ型は、決まったサイズを持たない、また可変長の大量のデータを格納するこ
とができます。この種のデータとしては、たとえば、ビットマップのグラフィックイメー
ジ、ベクター描画、サウンドファイル、小冊子や書籍の大きさの文書、マルチメディア関
連のデータなどがあります。このように、BLOB ではさまざまな型のデータを扱えますが、
反面、データの読み取りと書き込みに適した処理が必要になります。BLOB の処理の詳細
は、第 8 章「BLOB データの操作」を参照してください。
InterBase の DATE、TIME、および TIMEWSTAMP データ型をプログラムで扱う場合に
も変換が必要になります。日付の取り出しと書き込みの詳細は、第 7 章「日付と時刻」を
参照してください。
InterBase では、各種データ型の配列もサポートされています。配列とは複数のデータで構
成されるマトリクスを言います。配列には、BLOB を除き、InterBase がサポートしている
各種のデータを置くことができます。配列は、配列全体を 1 つのデータとして扱うことも
でき、データを個別に操作することもできます。配列を使って状況に応じたデータアクセス
を行うには、第 9 章「配列の使い方」を参照してください。
InterBase のデータ型の詳細は、『データ定義ガイド』を参照してください。
メモ
InterBase では、評価結果が FALSE になる " リテラル < 関係演算子 > リテラル " 形式の論
理式を探し、FALSE 論理式の反転ノードを返すことで、データ検索を省略します。
第 6 章 データ処理
6-3
SQL 式
SQL 式
SQL のデータ操作文では、どの文でも SQL 式を使用できます。式とは、列、定数、変数
の比較、評価に利用できる SQL 構文を指します。また、式による比較や評価の結果、1 つ
の値が返されます。
たとえば、SELECT 文では WHERE 句を記述でき、この WHERE 句には検索条件を指定
します。この検索条件によりデータ取り出しの際の行が識別されます。この検索条件が
SQL の式の 1 つです。また、DELETE や UPDATE でも検索条件の指定が可能です。式が
検索条件の場合、通常その式の結果は論理値(TRUE、FALSE、UNKNOWN)で返されます。
SQL の式は、このほか、INSERT 文の VALUE 句、UPDATE 文の SET 句でも記述できま
す。これらの句では、式を使用して、計算した値を列に挿入することができます。このよう
に、式を使って数値を挿入したり更新する場合、通常、算術式の形式で式を記述します。た
とえば、特定の値に別の値を掛ける算術式を記述することで、その結果を列に挿入したり、
既存のデータを計算後の値に更新することができます。また、挿入したり更新するデータが
文字列のときは、式は 2 つの文字列の連結(結合)式になります。この式によって、連結
された 1 つの文字列が列に挿入されたり、既存のデータがその文字列に更新されることに
なります。
式には、次の表にあるような要素(記号や演算子)を使用できます。
表 6.2
SQL 式の構成要素
要素
説明
列名
指定されているテーブル中の列名で、この指定された列のデータを
もとに値の検索や比較、または、値の計算が行われます。
ホスト言語変数
プログラムで使用されている変数で、格納されている値は変更可能
です。なお、変数は必ず先頭にコロン(:)を付けなければなりません。
定数
ハードコードされた数値または引用符で囲まれた文字列です。たと
えば、507 や “Tokyo” が該当します。
連結演算子
|| です。文字列を結合する際に使用します。
算術演算子
+、–、*、/ です。複数の値の計算や評価に使用します。
論理演算子
NOT、AND、OR の 3 つのキーワードがあり、検索条件で使用しま
す。いずれか 1 つを使って簡単な検索もでき、複数を組み合わせて
複雑な検索を行うこともできます。論理演算子では、結果が TRUE
(真)または FALSE(偽)で返ります。通常、検索条件以外では使用
しません。
比較演算子
<、>、<=、>=、=、<> で、演算子を挟んで左の値と右の値との比較
が行われます。比較演算子では、結果は TRUE(真)または FALSE
(偽)で返されます。
このほか、特殊な比較演算子として、ALL、ANY、BETWEEN、
CONTAINING、EXISTS、IN、IS [NOT] NULL、LIKE、SINGULAR、
SOME、STARTING WITH があります。これらの比較演算子では、
結果が TRUE(真)、FALSE(偽)、UNKNOWN(不定)のいずれ
かで返されます。比較演算子は、通常、検索条件でのみ使用されます。
6-4 埋 め 込 み S Q L ガ イ ド
SQL 式
表 6.2
SQL 式の構成要素 ( 続き )
要素
説明
COLLATE 句
CHAR または VARCHAR の値の比較では、COLLATE 句が必要に
なることがあります。この句を使用して、比較の際の文字の照合順
序を設定できます。
ストアドプロシージャ
再利用可能な SQL 文ブロックです。ストアドプロシージャを使って
パラメータの受け渡しが可能です。ストアドプロシージャは、デー
タベースのメタデータとして格納されています。
サブクエリー
通常、WHERE 句の中にネストされている SELECT 文を指し、メイ
ンの SELECT 文により検索された行に対して比較を行い、比較結果
値を返したり、計算を行ったりします。
かっこ
複数の式をグループ化し、優先順位を設定するために使用します。
かっこの内側の式は、外側の式より優先して処理されます。複数の
かっこをネストすると、最も内側のかっこ内の式が最初に処理され、
内側の式から外側へと順に処理されます。
日付リテラル
文字列値です。単引用符で囲んで入力でき、SELECT、INSERT、お
よび UPDATE 処理で日付値として解釈されます。使用できる文字
列には、‘TODAY’、‘NOW’、‘YESTERDAY’、‘TOMORROW’ が
あります。
USER 疑似列
現在ログインしているユーザー名を参照します。たとえば、USER
は、列の定義において、デフォルトとして使用できます。または、
INSERT に現在のユーザー名を入力する場合に使用できます。ユー
SELECT 文と DELETE 文で USER
ザー名がテーブルにある場合は、
を使って参照できます。
簡単な式を複数組み合わせることで、複雑な式を各種作成することができます。たとえば、
次の WHERE 句は、列名が 1 つ、定数が 3 つ、比較演算子が 3 つ、かっこでグループ化
した式が 1 つ、という複雑な構成の式の例です。この WHERE 句では、給与が 60,000 ド
ルを超えていて 120,000 ドル未満の範囲にある社員の行がすべて取り出されます。
WHERE DEPARTMENT = 'Publications' AND
(SALARY > 60000 AND SALARY < 120000)
また、WHERE 句では、検索条件の中に、ネストされた SELECT 文(サブクエリー)を
記述することがあります。たとえば、次の例は、WHERE 句に集計関数 AVG() を使用して
いるクエリーです。このクエリーでは、給与を部署別で見た場合、その給与が全部署の平
均値より高い部署がすべて取り出されます。
EXEC SQL
DECLARE WELL_PAID CURSOR FOR
SELECT DEPT_NO
INTO :wellpaid
FROM DEPARTMENT
WHERE SALARY > (SELECT AVG(SALARY) FROM DEPARTMENT);
サブクエリーを使って検索条件を指定する方法の詳細は、6-52 ページの「サブクエリー」
を参照してください。集計関数の詳細は、6-22 ページの「集合列のデータの取り出し」を
参照してください。
第 6 章 データ処理
6-5
SQL 式
式での文字列演算子の使い方
文字列演算子(連結演算子とも呼びます)である || を使用して、個別に分かれた複数の文
字列を 1 つの文字列に結合することができます。この場合の文字列は、文字定数でも列に
置かれている値(文字列)でもかまいません。連結演算子の使い方は、次のようになりま
す。次に例を示します。
char strbuf[80];
. . .
EXEC SQL
SELECT LAST_NAME || ' is the manager of publications.'
INTO :strbuf
FROM DEPARTMENT, EMPLOYEE
WHERE DEPT_NO = 5900 AND MNGR_NO = EMP_NO;
文字列演算子は、次のように INSERT や UPDATE でも使用できます。
EXEC SQL
INSERT INTO DEPARTMENT (MANAGER_NAME)
VALUES(:fname || :lname);
式での算術演算子の使い方
式で数値の計算に使用できる算術演算子として、InterBase では次の 4 種類があります。
表 6.3
算術演算子
演算子
用途
優先順位
演算子
用途
優先順位
*
乗算
1
+
加算
3
/
除算
2
–
減算
4
算術演算子が複数記述されている場合、通常、左から右へと順に処理されます。ただし、優
先順位が異なるものが順不同に混在しているときは、上記の表の優先順位にしたがって算
術演算子が実行されます(たとえば、乗算は除算より先に、除算は減算より先に行われる)。
また、式に算術演算、比較演算、論理演算が混在しているときは、算術演算が最初に実行
されます。この計算の実行順は、かっこで括ることにより変更できます。これで、かっこで
括った部分が、最初に処理されることになります。なお、かっこの部分にさらにネストされ
た部分があるときは、内側の部分から順に計算が行われます。優先順位とかっこを使ってグ
ループ化を行う方法の詳細は、6-15 ページの「演算子の優先順位」を参照してください。
たとえば、次は、WHERE 句の検索条件で算術演算子を使用している例です。ここでは、
まず、算術演算子を使って 2 つの列の値を加算した後、比較演算子を使って結果が 10 を超
えるかどうかを確認しています。
DECLARE RAINCITIES CURSOR FOR
SELECT CITYNAME, COUNTRYNAME
INTO :cityname, :countryname
FROM CITIES
6-6 埋 め 込 み S Q L ガ イ ド
SQL 式
WHERE JANUARY_RAIN + FEBRUARY_RAIN > 10;
式での論理演算子の使い方
論理演算子は左側と右側の 2 つの簡単な検索条件を比較する場合に利用します。比較の結
果は、論理値である TRUE(真)、FALSE(偽)、UNKNOWN(不定)のいずれかで返さ
れます。InterBase では NOT、AND、OR の 3 種類の論理演算子が認識されます。
NOT は、通常、検索条件の前に記述し、この場合、検索条件の働きが逆転します。一方、
AND と OR は、2 つの簡単な検索条件の間に置きます。たとえば、NOT を使った次のク
エリーでは、姓が Smith ではない社員がすべて取り出されます。
DECLARE NOSMITH CURSOR FOR
SELECT LAST_NAME
INTO :lname
FROM EMPLOYEE
WHERE NOT LNAME = 'Smith';
これに対して、AND を 2 つの検索条件の間に記述した場合は、両方の検索条件に適合する
ときに行が取り出されます。たとえば、次のクエリーでは、姓が Smith と Jones のどちら
でもない社員が取り出されます。
DECLARE NO_SMITH_OR_JONES CURSOR FOR
SELECT LAST_NAME
INTO :lname
FROM EMPLOYEE
WHERE NOT LNAME = 'Smith' AND NOT LNAME = 'Jones';
OR では、2 つの検索条件のうち 1 つが満足すれば、行が取り出されます。次のクエリーで
は、姓が Smith と Jones のいずれかに該当すれば、その社員の行(姓と名)が取り出され
ます。
DECLARE ALL_SMITH_JONES CURSOR FOR
SELECT LAST_NAME, FIRST_NAME
INTO :lname, :fname
FROM EMPLOYEE
WHERE LNAME = 'Smith' OR LNAME = 'Jones';
異なる種類の論理演算子が使用されているときは、各検索条件は、記述されている論理演
算子の優先順位にしたがって実行されます。この場合、NOT の検索条件が最初に、続いて
AND、OR の順で処理が行われます。なお、かっこを使用することで、優先順位の変更が
可能です。優先順位とかっこを使ってグループ化を行う方法の詳細は、6-15 ページの「演
算子の優先順位」を参照してください。
第 6 章 データ処理
6-7
SQL 式
式での比較演算子の使い方
比較演算子は、左側の値と右側の値または範囲の間の関係を検査するのに利用できます。結
果は、論理値である TRUE(真)、FALSE(偽)、UNKNOWN(不定)のいずれかで返さ
れます。比較演算子では、比較対象の値はどちらも同じデータ型でなければなりません。な
お、双方の値のデータ型が異なる場合は、CAST() 関数を使ってデータ型を変換できます。
また、値としては、列名、定数、計算値を使用できます。
次の表では、ステートメントに使用できる演算子、それらの演算子の使い方、およびその
使用例について説明します。
表 6.4
メモ
比較演算子
演算子
用途
=
等しい
<
小なり
>
大なり
>=
大なりまたは等しい(以上)
<=
小なりまたは等しい(以下)
!>、~>、^>
より大きくない
!<、~<、^<
より小さくない
<>、!=
等しくない
比較演算子による演算で値が NULL だった場合、UNKNOWN が返されます。
CAST() の詳細は、6-17 ページの「CAST() によるデータ型の変換」を参照してください。
一般の比較演算子のほか、InterBase では、特殊な比較演算子も用意されています。この種
の比較演算子では、演算子の右側にサブクエリーを置き、そのサブクエリーの結果と左側
の値の比較が可能です。こういった特殊な比較演算子および処理内容は、次の表のとおりで
す。
表 6.5
サブクエリーを必要とする InterBase の比較演算子
演算子
用途
ALL
左側の値が、サブクエリーから返される値全部と比較され、その全部の
値が条件を満足すると処理が行われます。
ANY および
SOME
左側の値が、サブクエリーから返される値全部と比較され、その中のい
ずれかの値が条件を満足すると処理が行われます。
EXISTS
サブクエリーから返された値の中に、列の値と同じものが少なくとも 1
つあれば処理が行われます。
SINGULAR
サブクエリーから返された値の中に、列の値と同じものがあり、それが
1 つだけであれば処理が行われます。
サブクエリーの使い方の詳細は、6-52 ページの「サブクエリー」を参照してください。
6-8 埋 め 込 み S Q L ガ イ ド
SQL 式
BETWEEN の使い方
BETWEEN では、値が一定の範囲に収まるかどうかを調べます。BETWEEN 演算子の正
規の構文は次のとおりです。
<value> [NOT] BETWEEN <value> AND <value>
たとえば、次のカーソル宣言では、給与が 100,000 ドル以上 250,000 ドル以下の範囲にあ
る社員について、
LAST_NAME と FIRST_NAME の 2 つの列のデータが取り出されます。
EXEC SQL
DECLARE LARGE_SALARIES CURSOR FOR
SELECT LAST_NAME, FIRST_NAME
FROM EMPLOYEE
WHERE SALARY BETWEEN 100000 AND 250000;
また、NOT BETWEEN では、値が指定した範囲の外にあるかどうかを調べます。たとえ
ば、次のカーソル宣言では、給与が 30,000 ドル未満の社員、および 150,000 ドルを超える
社員について、LAST_NAME と FIRST_NAME の 2 つの列のデータが取り出されます。
EXEC SQL
DECLARE EXTREME_SALARIES CURSOR FOR
SELECT LAST_NAME, FIRST_NAME
FROM EMPLOYEE
WHERE SALARY NOT BETWEEN 30000 AND 150000;
CONTAINING の使い方
CONTAINING では、左側の値として ASCII 文字列、右側の値として引用符で囲んだ
ASCII 文字列を記述します。これで、左側の ASCII 文字列の中に右側の ASCII 文字列が
含まれているかどうかが検査されます。なお、CONTAINING では、大文字と小文字が区
別されません。つまり、“String”、“STRING”、“string” はいずれも同じ値と見なされます。
ダイアレクト 3 のデータベースとクライアントでは、文字列は単引用符で囲まなければな
りません。CONTAINING の正規の構文は次のとおりです。
<value> [NOT] CONTAINING '<string>'
たとえば、次のカーソル宣言では、姓名の中に "las"(大文字と小文字は区別されないので、
"LAS" と "Las" も該当)という文字列が含まれているという条件に一致する社員名がすべ
て取り出されます。
EXEC SQL
DECLARE LAS_EMP CURSOR FOR
SELECT LAST_NAME, FIRST_NAME
FROM EMPLOYEE
WHERE LAST_NAME CONTAINING 'las';
指定した文字列が含まれていない行を検索する場合は、NOT CONTAINING を使用しま
す。たとえば、次のカーソル宣言では、姓名の中に “las”(“LAS” と “Las” も該当)とい
う文字列が含まれていない社員名がすべて取り出されます。
EXEC SQL
DECLARE NOT_LAS_EMP CURSOR FOR
SELECT LAST_NAME, FIRST_NAME
第 6 章 データ処理
6-9
SQL 式
FROM EMPLOYEE
WHERE LAST_NAME NOT CONTAINING 'las';
ヒント
CONTAINING は、BLOB データ中の引用符で囲まれた文字列の検索にも利用できます。
この場合、セグメント単位で検索されます。
IN の使い方
IN では、左側の値が、右側に記述した複数の値の中にあるかどうかを検査することができ
ます。IN の場合、右側の値はそれぞれカンマで区切り、かっこで囲みます。IN を使用する
ときは、必ず、この書式が必要になります。なお、左側の値が NULL の場合は、UNKNOWN
が返されます。
IN の構文は次のとおりです。
<value> [NOT] IN (<value> [, <value> ...])
たとえば、次のカーソル宣言では、"Accounting"、"Payroll"、"Human Resources" のいず
れかの部署に所属する社員名がすべて取り出されます。
EXEC SQL
DECLARE ACCT_PAY_HR CURSOR FOR
SELECT DEPARTMENT, LAST_NAME, FIRST_NAME, EMP_NO
FROM EMPLOYEE EMP, DEPTARTMENT DEP
WHERE EMP.DEPT_NO = DEP.DEPT_NO AND
DEPARTMENT IN ('Accounting', 'Payroll', 'Human Resources')
GROUP BY DEPARTMENT;
右側に指定した複数の値に該当しない値を持つ行を検索するときは、NOT IN を使用しま
す。たとえば、次のカーソル宣言では、"Accounting"、"Payroll"、"Human Resources" 以
外の部署に所属する社員の名前がすべて取り出されます。
EXEC SQL
DECLARE NOT_ACCT_PAY_HR CURSOR FOR
SELECT DEPARTMENT, LAST_NAME, FIRST_NAME, EMP_NO
FROM EMPLOYEE EMP, DEPTARTMENT DEP
WHERE EMP.DEPT_NO = DEP.DEPT_NO AND
DEPARTMENT NOT IN ('Accounting', 'Payroll',
'Human Resources')
GROUP BY DEPARTMENT;
IN では、左側に複数の値のかわりにサブクエリーを置き、その結果をもとに比較を行うこ
ともできます。たとえば、次のカーソル宣言では、ヨーロッパにある都市がすべて取り出さ
れます。
EXEC SQL
DECLARE NON_JFG_CITIES CURSOR FOR
SELECT C.COUNTRY, C.CITY, C.POPULATION
FROM CITIES C
WHERE C.COUNTRY NOT IN (SELECT O.COUNTRY FROM COUNTRIES O
WHERE O.CONTINENT <> 'Europe')
GROUP BY C.COUNTRY;
サブクエリーの使い方の詳細は、6-52 ページの「サブクエリー」を参照してください。
6-10 埋 め 込 み S Q L ガ イ ド
SQL 式
LIKE の使い方
LIKE では、右側にワイルドカードを含む文字列を指定します。左側の文字列は、このワイ
ルドカードを含む文字列と比較されることになります。比較では、大文字と小文字は区別
されます。なお、ワイルドカードとは単一の文字または文字列を表す記号で、任意の文字
または文字列を代行する働きを持っています。LIKE では、次の 2 種類のワイルドカードを
使用できます。
• %(パーセント記号):複数の文字(文字がない場合も含む)で構成される任意の文字列を
表します。
• _(下線記号):単一の任意の文字を表します。
LIKE の構文は次のとおりです。
<value> [NOT] LIKE <value> [ESCAPE 'symbol']
たとえば、次のカーソル宣言では、姓の中に "ton"(大文字と小文字が区別されるので、
"Ton" は除外されます)という 3 文字を含んでいる社員のデータが取り出されます。
EXEC SQL
DECLARE TON_EMP CURSOR FOR
SELECT LAST_NAME, FIRST_NAME, EMP_NO
FROM EMPLOYEE
WHERE LAST_NAME LIKE '%ton%';
なお、場合によっては、検索対象の文字列にパーセント記号や下線が含まれていることが
あります。このように文字列を検索する場合は、次のようにします。
1 % または _ の右になんらかの記号(次の例では @)を記述します。
2 ESCAPE 句で、その記号(エスケープ文字)を指定します(次の例では @)。これで、
エスケープ文字の右隣の文字がワイルドカードではなくリテラルシンボル(実際の文
字)であると解釈されるようになります。つまり、エスケープ文字の右の文字が検索対
象に含まれることになります。
たとえば、次のカーソル宣言では、RDB$RELATIONS のテーブル名のうち、下線が含ま
れているテーブル名がすべて取り出されます。
EXEC SQL
DECLARE UNDER_TABLE CURSOR FOR
SELECT RDB$RELATION_NAME
FROM RDB$RELATIONS
WHERE RDB$RELATION_NAME LIKE '%@_%' ESCAPE '@';
NOT LIKE を使用すると、上とは逆の形で行を取り出すこともできます。たとえば、次の
例では、RDB$RELATIONS のテーブル名のうち、下線が含まれていないテーブル名がす
べて取り出されます。
EXEC SQL
DECLARE NOT_UNDER_TABLE CURSOR FOR
SELECT RDB$RELATION_NAME
FROM RDB$RELATIONS
WHERE RDB$RELATION_NAME NOT LIKE '%@_%' ESCAPE '@';
第 6 章 データ処理
6-11
SQL 式
IS NULL の使い方
IS NULL を使用して、列の値が存在しない行を取り出すことができます。IS NULL の正
規の構文は次のようになります。
<value> IS [NOT] NULL
たとえば、次は、電話(内線)を持っていない社員名を取り出す記述例です。
EXEC SQL
DECLARE MISSING_PHONE CURSOR FOR
SELECT LAST_NAME, FIRST_NAME
FROM EMPLOYEE
WHERE PHONE_EXT IS NULL;
逆に、列の値が存在する行を拾いたいときは、IS NOT NULL を使用します。たとえば、電
話(内線)を持っている社員名を取り出すには次のように入力します。
EXEC SQL
DECLARE PHONE_LIST CURSOR FOR
SELECT LAST_NAME, FIRST_NAME, PHONE_EXT
FROM EMPLOYEE
WHERE PHONE_EXT IS NOT NULL
ORDER BY LAST_NAME, FIRST_NAME;
STARTING WITH の使い方
STARTING WITH では、先頭の文字列を指定し、その文字列で始まる値を検索すること
ができます。この場合、大文字と小文字が区別されます。また、照合順序が指定されている
場合は、そのバイト一致規則が使用されるため、異なる言語のキャラクタセットも変換可
能です。STARTING WITH の正規の構文は次のとおりです。
<value> [NOT] STARTING WITH <value>
たとえば、次のカーソル宣言では、姓が "To" で始まる社員がすべて取り出されます。
EXEC SQL
DECLARE TO_EMP CURSOR FOR
SELECT LAST_NAME, FIRST_NAME
FROM EMPLOYEE
WHERE LAST_NAME STARTING WITH 'To';
指定した文字列以外で始まるデータを取り出すには、NOT STARTING WITH を使用しま
す。たとえば、姓が “To” 以外で始まる社員名を取り出す場合、次のように記述します。
EXEC SQL
DECLARE NOT_TO_EMP CURSOR FOR
SELECT LAST_NAME, FIRST_NAME
FROM EMPLOYEE
WHERE LAST_NAME NOT STARTING WITH 'To';
照合順序とバイト一致規則の詳細は、『データ定義ガイド』を参照してください。
6-12 埋 め 込 み S Q L ガ イ ド
SQL 式
ALL の使い方
ALL では、列の値と、サブクエリーにより返される複数の値がすべて比較され、その結果、
条件が満たされれば列の値が取り出されます。ALL の正規の構文は次のとおりです。
<value> <comparison_operator> ALL (<subquery>)
たとえば、次のカーソル宣言では、流通ルート販売部の社員全員の給与がすべて検査され、
列の値がどの給与より高かった場合、その列の値が取り出されます。ここでは、流通ルー
ト販売部の最高給与(通常は部長の給与が最高)を超える給与の社員名が取り出されます。
EXEC SQL
DECLARE MORE_THAN_VP CURSOR FOR
SELECT LAST_NAME, FIRST_NAME, SALARY
FROM EMPLOYEE
WHERE SALARY > ALL (SELECT SALARY FROM EMPLOYEE
WHERE DEPT_NO = 7734);
サブクエリーによりなんらかの値が返された場合、その値が NULL だったときは、ALL は
UNKNOWN を返します。逆に、比較対象の値(列の値)が NULL で、サブクエリーによ
り返される値が NULL 以外のときも、ALL は UNKNOWN を返します。一方、比較対象
の値が NULL で、サブクエリーにより返される値が空のセット(すべて NULL)だったと
きは、ALL は TRUE(真)を返します。
サブクエリーの使い方の詳細は、6-52 ページの「サブクエリー」を参照してください。
ANY および SOME の使い方
ANY と SOME では、サブクエリーにより返される複数の値のうち、条件に一致するもの
があれば、列の値が取り出されます。ANY の正規の構文は次のとおりです。
<value> <comparison_operator> ANY | SOME (<subquery>)
たとえば、次のカーソル宣言では、流通ルート販売部の社員全員の給与が検査され、列の
値が、その中の給与より高かった場合、その列の値が取り出されます。したがって、ここ
では、流通ルート販売部の最低給与を超える給与の社員名が取り出されます。
EXEC SQL
DECLARE MORE_CHANNEL CURSOR FOR
SELECT LAST_NAME, FIRST_NAME, SALARY
FROM EMPLOYEE
WHERE SALARY > ANY (SELECT SALARY FROM EMPLOYEE
WHERE DEPT_NO = 7734);
サブクエリーによりなんらかの値が返された場合、その値が NULL だったときは、ANY
と SOME は UNKNOWN を返します。逆に、比較対象の値(列の値)が NULL で、サブ
クエリーにより返される値が NULL 以外のときも、ANY と SOME は UNKNOWN を返
します。一方、比較対象の値が NULL で、サブクエリーにより返される値が空のセット(す
べて NULL)だったときは、ANY と SOME は FALSE を返します。
サブクエリーの使い方の詳細は、6-52 ページの「サブクエリー」を参照してください。
第 6 章 データ処理
6-13
SQL 式
EXISTS の使い方
EXISTS では、列のデータについて、サブクエリーで指定された検索条件を満足する行が
別のテーブル中に少なくとも 1 つあれば、列のデータが取り出されます。すべての列を選
択するには、サブクエリーの SELECT 句に *(アスタリスク)を記述します。EXISTS の
正規の構文は次のとおりです。
[NOT] EXISTS (SELECT * FROM <tablelist> WHERE <search_condition>)
次は、河川がある国をすべて取り出す場合の記述です。
EXEC SQL
DECLARE RIVER_COUNTRIES CURSOR FOR
SELECT COUNTRY
FROM COUNTRIES C
WHERE EXISTS (SELECT * FROM RIVERS R
WHERE R.COUNTRY = C.COUNTRY);
また、NOT EXISTS を使用すると、サブクエリーで指定した条件に該当しない行のデータ
が取り出されます。たとえば、次の例では、河川のない国が取り出されます。
EXEC SQL
DECLARE NON_RIVER_COUNTRIES COUNTRIES FOR
SELECT COUNTRY
FROM COUNTRIES C
WHERE NOT EXISTS (SELECT * FROM RIVERS R
WHERE R.COUNTRY = C.COUNTRY);
EXISTS では、NULL 値の場合でも必ず、TRUE(真)または FALSE(偽)が返されます。
サブクエリーの使い方の詳細は、6-52 ページの「サブクエリー」を参照してください。
SINGULAR の使い方
SINGULAR では、列のデータについて、サブクエリーで指定された検索条件を満足する
行が別のテーブル中に 1 つだけあれば、列のデータが取り出されます。すべての列を選択
するには、サブクエリーの SELECT 句に *(アスタリスク)を記述します。SINGULAR
の正規の構文は次のとおりです。
[NOT] SINGULAR (SELECT * FROM <tablelist> WHERE <search_condition>)
次は、首都が 1 つだけの国を取り出すカーソル宣言の例です。
EXEC SQL
DECLARE SINGLE_CAPITAL CURSOR FOR
SELECT COUNTRY
FROM COUNTRIES COU
WHERE SINGULAR (SELECT * FROM CITIES CIT
WHERE CIT.CITY = COU.CAPITAL);
また、NOT SINGULAR を使用すると、サブクエリーで指定した条件に該当しない行の
データが取り出されます。たとえば、次の例では、首都が複数ある国が取り出されます。
EXEC SQL
DECLARE MULTI_CAPITAL CURSOR FOR
SELECT COUNTRY
6-14 埋 め 込 み S Q L ガ イ ド
SQL 式
FROM COUNTRIES COU
WHERE NOT SINGULAR (SELECT * FROM CITIES CIT
WHERE CIT.CITY = COU.CAPITAL);
サブクエリーの使い方の詳細は、6-52 ページの「サブクエリー」を参照してください。
演算子の優先順位
文中で演算子および演算子に関係する数値が評価される順番を優先順位と言います。演算
子の優先順位としては、次の 2 種類があります。
• 異なる種類の演算子間での優先順位
• 同じ種類の演算子間での優先順位
演算子間での優先順位
異なる種類の演算子
InterBase で使用できる演算子の種類は次の表のとおりです。演算子は、表の上の方に記載
されている種類から順に評価されます(文字列演算子が優先順位が最も高く、論理演算子
が優先順位が最も低くなる)。
表 6.6
演算子の種類別優先順位
演算子の種類
優先順位
摘要
文字列
1(最上位)
他の種類の演算子があっても、文字列演算子が最も先に実行さ
れ 2 つの文字列が連結されます。
算術演算子
fl
算術演算子は、文字列演算子の次に実行されます。ただし、比
較演算子と論理演算子より優先順位は上です。
比較演算子
fl
比較演算子は、文字列演算子および算術演算子の次に実行され
ます。ただし、論理演算子より優先順位は上です。
論理演算子
3(最下位)
論理演算子は、上記の 3 種類の演算子が実行された後で実行さ
れます。
同じ種類の演算子
式に同じ種類の演算子が複数記述されている場合、各演算子は左から右へと順に評価され
ます。ただし、同一の値に対して 2 つの演算子の両方が働く場合、この規則は適用されま
せん。
たとえば、3 + 2 * 6 という数式があったとすると、加算演算子と乗算演算子の両方が同一
の値である 2 に働くことになります。この場合、原則どおり左から右へ計算が行われると、
3 + 2 = 5、5 * 6 = 30 で答えは 30 となります。3+ 2 = 5; 5 * 6 = 30. これでは不都合が生じ
るため、InterBase では、通常の数式の規則にしたがって、自動的に乗算が先に実行されま
す。つまり、2 * 6 = 12、3 + 12 = 15 という結果が得られます。
第 6 章 データ処理
6-15
SQL 式
次の表は、算術演算子を評価順の高いものから低いものにしたがって一覧にしたものです。
表 6.7
算術演算子の優先順位
演算子
優先順位
摘要
*
1(最上位)
乗算は、他の 3 つの算術演算子に先立って実行されます。
/
fl
除算は、加算と減算に先立って実行されます。
+
fl
加算は、減算の前に実行されます。
–
3(最下位)
減算は、他の 3 つの算術演算子による計算が終わってから実行されま
す。
比較演算子も、通常は左から右へと実行されますが、算術演算子と同様に演算子同士の競
合が起こることもあります。このような場合のために、InterBase では比較演算子を評価す
る際の規則が設けられています。次の表は、比較演算子の評価順を一覧したものです。評価
順は上が高く、下が低くなっています。
表 6.8
比較演算子の優先順位
演算子
優先順位
摘要
=, ==
1(最上位)
等号演算子は、どの演算子よりも先に評価されます。
<>, !=, ~=, ^=
fl
>
fl
<
fl
>=
fl
<=
fl
!>, ~>, ^>
fl
!<, ~<, ^<
3(最下位)
より小さくない演算子は、他の演算子の処理が終わっ
てから最後に評価されます。
上記の比較演算子と、ALL、ANY、BETWEEN、CONTAINING、EXISTS、IN、LIKE、
NULL、SINGULAR、SOME、STARTING WITH といった特殊比較演算子が混在してい
るときは、通常どおり左から右へと評価されます。ただし、両者で競合が起こったときは、
まず、通常の比較演算子が評価され、その後で特殊比較演算子が実行されます。なお、特殊
演算子同士が競合した場合、原則どおり、左から右へ評価されます。
6-16 埋 め 込 み S Q L ガ イ ド
SQL 式
また、論理演算子も通常は左から右へと評価されますが、論理演算子同士が競合した場合、
次の表の優先順位にしたがって実行されます。
表 6.9
論理演算子の優先順位
演算子
優先順位
摘要
NOT
1(最上位)
NOT 演算子は、どの論理演算子よりも先に評価されます。
AND
fl
NOT 演算子、AND 演算子、OR 演算子の順に評価されます。
OR
3(最下位)
OR 演算子は、3 つの論理演算子のうち最後に評価されます。
演算子の評価順の変更
式で演算子が複数記述されている場合、かっこを使ってグループにまとめることで、演算
子の評価順の変更が可能です。これで、かっこで括った部分が 1 つの単位として評価され
ることになります。この部分で算出された値は、他の演算子で利用されます。たとえば、3
+ 2 * 6 という数式があり、かっこで括らない場合には 15 という結果が出ます。この数式
で、加算を先行させる場合は、次のようにしてかっこで括ります。
(3 + 2) * 6 = 30
ヒント
複雑な式では、左から右へというデフォルトの評価順で問題がなくても、できるだけかっ
こを使って各部をまとめるようにします。明示的にグループ化された式は、見やすく、また
デバッグも楽になります。
CAST() によるデータ型の変換
通常、式で比較または評価できるのは、同じデータ型のデータに限られます。データ型の異
なるデータを式で使用する場合は、CAST() 関数を使用します。この関数を使用すること
で、データのデータ型の変換が可能です。CAST() の構文は次のとおりです。
CAST (<value> | NULL AS datatype)
た と え ば、次 の
WHERE 句では、CAST() を使用して、CHAR データ型の
INTERVIEW_DATE を DATE データ型に変換しています。これで、DATE データ型の
HIRE_DATE との比較が可能になります。
WHERE HIRE_DATE = CAST(INTERVIEW_DATE AS DATE);
このように CAST() を使用することで、同じテーブルにデータ型の異なる列があっても比
較が可能です。また、CAST() は、別のテーブルの列に対しても使用できます。CAST() で
変換できるデータ型は次の表のとおりです。
表 6.10 CAST() で変換できるデータ型
変換元
変換先
Numeric
CHARACTER, VARCHAR, Date, Time,
Timestamp, Numeric
CHARACTER, VARCHAR
CHAR, Date, Time, Timestamp
Date
CHAR, VARCAHR, Timestamp
第 6 章 データ処理
6-17
SQL 式
表 6.10 CAST() で変換できるデータ型 ( 続き )
変換元
変換先
Time
CHAR, VARCHAR, Timestamp
Timestamp
CHAR, VARCHAR, Date, Time
BLOB, Array
—
Boolean
CHAR, VARCHAR
データ型を CAST() で指定したデータ型に変換できなかった場合にはエラーとなります。
整 数 以 外 の 数 値 デ ー タ 型 を整数データ型にキャストする場合、CAST() は Delphi の
ROUND(x) のように機能します。x は最も近い整数に丸められ、x が 2 つの整数のちょう
ど中間にある場合、結果は絶対値が大きい方の数になります。次に例を示します。
CAST(1.6 as INTEGER) = 2
CAST(-1.5 as INTEGER) = -2
CAST(-1.6 as INTEGER) = -2
UPPER() によるテキストデータの大文字変更
UPPER() を使用して、テキスト(文字)データおよび BLOB のテキストデータを強制的
に大文字にすることができます。UPPER() は、SELECT、INSERT、UPDATE、DELETE
の各文で使用できます。たとえば、会社の部署名は大文字で記憶されていた方が、将来の
データの取り出しが簡単になります。このような場合、ユーザーが部署名を小文字で入力
した際に、UPPER() を使用して、強制的に大文字にすることができます。次は、INSERT
文で UPPER() を使用し、ユーザーの入力文字を大文字に変更するコード例です。
EXEC SQL
BEGIN DECLARE SECTION;
char response[26];
EXEC SQL
END DECLARE SECTION;
. . .
printf("Enter new department name: ");
response[0] = '¥0';
gets(response);
if (response)
EXEC SQL
INSERT INTO DEPARTMENT(DEPT_NO, DEPARTMENT)
VALUES(GEN_ID(GDEPT_NO, 1), UPPER(:response));
. . .
また、次の文は、SELECT 文で UPPER() を使用している例です。この例では、データが
大文字に変換されて取り出されるとともに、検索対象が大文字に限定されます。
EXEC SQL
SELECT DEPT_NO, UPPER(DEPARTMENT)
6-18 埋 め 込 み S Q L ガ イ ド
SELECT に よ る デ ー タ の 取 り 出 し
FROM DEPARTMENT
WHERE UPPER(DEPARTMENT) STARTING WITH 'A';
SELECT によるデータの取り出し
SQL でのクエリーは、すべて SELECT 文を使って行います。SELECT では、テーブルの
行を指定して取り出すことも、テーブルの行全部を一括して取り出すこともできます。取
り出しの際は列を指定することもできます。列の指定による取り出しのことを、射影
(projection)と呼んでいます。SELECT にはオプションで検索条件を指定することができ
ます。この検索条件により、取り出す行の数を限定したり、値が不明の行を取り出すこと
ができます。また、ビューによる行の取り出し、複数のテーブルの行の結合といった作業
を行うこともできます。
SELECT 文には、最低、次の要素を指定しなければなりません。
• テーブル中の列名:この列からデータが取り出されることになります。 名前は複数列記す
ることもでき、SELECT の右に記述します。
• テーブル名:このテーブルの列から取り出しが行われることになります。FROM 句に記述
します。
SELECT で単一行 SELECT(1 つのテーブルの 1 行、または 1 行の中の任意の列の取り出
し)を行うときは、INTO 句と WHERE 句を記述しなければなりません。このうち、INTO
句では変数を指定します。この変数に、取り出されたデータが格納されます。また、WHERE
句には検索条件を記述します。この場合、検索条件は、列の 1 行だけが検索されるような
ものでなければなりません。
たとえば、次の SELECT 文では、テーブルの 3 つの列から値(データ)が取り出され、各
値が 3 つの変数に格納されます。
EXEC SQL
SELECT EMP_NO, FIRSTNAME, LASTNAME
INTO :emp_no, :fname, :lname
FROM EMPLOYEE WHERE EMP_NO = 1888;
ヒント
変数は、プログラム中で事前に宣言しておかなければ、SELECT などの SQL 文で使用す
ることはできません。ホスト変数の宣言の詳細は、第 2 章「アプリケーションの必要条件」
を参照してください。
第 6 章 データ処理
6-19
SELECT に よ る デ ー タ の 取 り 出 し
次の表は、SELECT 文で使用できる句の一覧です。なお、句を SELECT で記述する順番は
決まっており、表では、その順番にしたがって掲載してあります。また、単一行 SELECT
(1 行の取り出し)と複数行 SELECT(複数行にわたる取り出し)での使用についても示し
ます。
表 6.11 SELECT 文の句
句
用途
SELECT
抽出する列を一覧します。
INTO
単一行 SELECT で、抽出された列を格納するホスト変数を一覧します。
FROM
テーブルの指定に使用します。このテーブルに対して、取り出しが行われ
ます。
WHERE
検索条件の指定に使用します。この検索条件により、取り出し対象が一部
の行に限定されます。この WHERE 句には、その WHERE 句だけで使用
される SELECT 文を記述することができます。この文をサブクエリーと
言います。
GROUP BY
この句を使用して、列の同じデータを基準に行をグループ化することがで
きます。HAVING と併用することができます。
HAVING
GROUP BY によりグループ化された行を、さらに一部の行に限定するこ
とができます。
UNION
複数の SELECT 文により生成された結果を、行の重複なしに 1 つの動的
なテーブルにまとめることができます。
PLAN
クエリーの方法を指定することができます。通常、クエリーの方法はクエ
リー最適化機能により自動的に決められますが、この PLAN を使って指
定することもできます。
ORDER BY
SELECT によって取り出される行のソート順を指定できます。デフォルト
の昇順(ASC)のほか、降順(DESC)が指定できます。
ROWS <value>
[TO <upper_value>]
[BY <step_value>]
[PERCENT][WITH TIES]
•
•
•
•
•
•
•
value は、単独で使用される場合は、返される行の総数。
value は、TO とともに使用される場合は、返される最初の行の番号。
value は、PERCENT とともに使用される場合は、パーセント。
upper_value は、返される最後の行。
step_value = n の場合は、n 行ごとの行を返す。
value PERCENT は、value=n の場合に、n% の行を返す。
WITH TIES は、重複行を返す。ORDER BY とともに使用する必要が
ある。
FOR UPDATE
DECLARE CURSOR 文で SELECT 句を使用している場合、FOR
UPDATE を記述することで、SELECT で取り出されたデータが更新可能
な状態となります。データの更新には、WHERE CURRENT OF 句を使用
します。
この各句を SELECT とともに使用する方法については、以下の各節で説明します。これに
引き続いて、SELECT を直接使って単一の行を返す方法、および DECLARE CURSOR 文
で SELECT を使って複数の行を返す方法の詳細について説明します。SELECT 構文につ
いての概要は、『言語リファレンス』を参照してください。
6-20 埋 め 込 み S Q L ガ イ ド
SELECT に よ る デ ー タ の 取 り 出 し
SELECT での取り出し列の指定
SELECT 文では、SELECT キーワードの右にデータの取り出し元となる列を指定しなけれ
ばなりません。列は 1 つでも複数でもかまいません。この SELECT キーワードと一覧した
列を SELECT 句と呼んでいます。
複数の列の指定
複数の列からデータを取り出す場合、その列名を SELECT の右に指定しなければなりませ
ん。この場合、列のデータは、指定した順に取り出されます。また、列名はそれぞれカン
マで区切らなければなりません。このように、全部の列ではなく任意の列のデータを取り出
す処理を射影と言います。
たとえば、次の SELECT では、3 つの列からデータが取り出されます。
EXEC SQL
SELECT EMP_NO, FIRSTNAME, LASTNAME
INTO :emp_no, :fname, :lname
FROM EMPLOYEE WHERE EMP_NO = 2220;
全部の列の指定
テーブル全部の列からデータを取り出すときは、SELECT の右にアスタリスク(*)を指
定します。これによって何度も列名を指定しなくても済みます。たとえば、次の SELECT
文では、EMPLOYEE テーブルの全部の列から 1 行分のデータが取り出されます。
EXEC SQL
SELECT *
INTO :emp_no, :fname, :lname, :phone_ext, :hire, :dept_no,
:job_code, :job_grade, :job_country, :salary, :full_name
FROM EMPLOEE WHERE EMP_NO = 1888;
重要
複数の列を指定する場合、各列について変数を 1 つずつ指定しなければなりません。
DISTINCT による重複データの排除
テーブルによっては、同じ列に同じデータが重複して記憶されていることもあります。ま
た、クエリーによっては、データの確認だけが必要で、重複しているデータを全部取り出
す必要がないこともあります。たとえば、次は、EMPLOYEE テーブルに SMITH という
姓の社員がいるかどうかを調べるためのクエリーです。この場合、SMITH という姓を持つ
社員がすべて取り出されます。
EXEC SQL
DECLARE SMITH CURSOR FOR
SELECT LAST_NAME
FROM EMPLOYEE
WHERE LAST_NAME = 'Smith';
このような場合、SELECT に DISTINCT キーワードを付加してデータを 1 つに絞ること
ができます。たとえば、次の SELECT 文では、“Smith” という姓の社員が 1 人だけ取り出
されます。
第 6 章 データ処理
6-21
SELECT に よ る デ ー タ の 取 り 出 し
EXEC SQL
DECLARE SMITH CURSOR FOR
SELECT DISTINCT LAST_NAME
FROM EMPLOYEE
WHERE LAST_NAME = 'Smith';
DISTINCT は、1 つだけ記述すれば、指定した列全部に働きます。
集合列のデータの取り出し
SELECT には、集計関数(aggregate function)を記述することができます。集計関数と
は、クエリーで認識された複数行に対して、列または計算列別になんらかの計算を行い、1
つの値または総計に関する数値を返すような関数を言います。したがって、各行の値が個
別に取り出されることはありません。InterBase で使用できる集計関数は次のとおりです。
表 6.12 SQL の集計関数
関数
用途
AVG()
複数の値の平均値を計算します。
MIN()
複数の値の最小値を取り出します。
MAX()
複数の値の最大値を取り出します。
SUM()
複数の値の合計を計算します。
COUNT()
クエリーの検索条件に適合する行の数を計算します(WHERE 句で条件を特定)
。
たとえば、次のクエリーでは、EMPLOYEE テーブルに記録されている全社員の平均給与
が返されます。
EXEC SQL
SELECT AVG(SALARY)
INTO :avg_sal
FROM EMPLOYEE;
また、次の SELECT 文では、EMPLOYEE テーブルに対するクエリーで識別された行の総
数が返されます。さらに、社員番号の最大値と最小値、全社員の給与総額も計算されます。
EXEC SQL
SELECT COUNT(*), MAX(EMP_NO), MIN(EMP_NO), SUM(SALARY)
INTO :counter, :maxno, :minno, :total_salary
FROM EMPLOYEE;
なお、集計関数で使用される値が NULL または不定だった場合は、その値が置かれている
行は自動的に計算から除外されます。この自動除外機能により、不定の値があっても、平均
値は正常に計算されます。
メモ
集計関数では、全部の行のほか、複数行をグループ化して値を計算することもできます。こ
のようにして計算された値をグループ集合(group aggregate)値と呼んでいます。グルー
プ集合の使い方の詳細は、6-31 ページの「GROUP BY による複数行のグループ化」を参
照してください。
6-22 埋 め 込 み S Q L ガ イ ド
SELECT に よ る デ ー タ の 取 り 出 し
複数テーブルを対象とする SELECT 文
SELECT で複数のテーブル(またはビューや選択プロシージャ)からデータを取り出す場
合、各テーブルに同じ名前の列が存在することがあります。このような場合は、SELECT
文で、同一の列名を識別する記述を行わなければなりません。
複数のテーブルの同一列名を識別するには、SELECT 文で列名の前に識別子を付加します。
この場合、次の 2 とおりの方法があります。
• テーブル名とピリオドを付けます。次に例を示します。
EMPLOYEE.EMP_NO は、EMPLOYEE テーブルの EMP_NO という列を示すことにな
ります。
• テーブル相関名(エイリアス)とピリオドを付けます。たとえば、EMPLOYEE テーブル
の相関名が EMP とすると、EMP.EMP_NO は、EMPLOYEE テーブルの EMP_NO とい
う列を表します。
テーブル相関名(またはビューや選択プロシージャ)は、SELECT 文の FROM 句で宣言
できます。相関名の宣言の詳細、およびその使用例は、6-26 ページの「相関名の宣言と使
い方」を参照してください。
トランザクション名の指定
InterBase では、SQL アプリケーションで同時に複数のトランザクションを実行できるよ
うに設計されています。ただ、次の条件が必要になります。
• トランザクションがそれぞれ SET TRANSACTION 文で事前に宣言されていること。
• データ操作文(SELECT、INSERT、UPDATE、DELETE)の TRANSACTION 句で、
そのつどトランザクション名を指定すること。これで、その文は、指定したトランザクショ
ンのもとで働くことになります。
• SQL 文が動的ではないこと(DSQL 文ではないこと)。
た と え ば、SELECT
で は、SELECT
キーワードの右に TRANSACTION
句
(TRANSACTION とトランザクション名)を記述し、その右に列名を続けます。構文は次
のようになります。
SELECT TRANSACTION name <col> [, <col> ...]
なお、最初から最後まで 1 つのトランザクションしか使用しないプログラム、または一度
に 1 つのトランザクションしか起動しないプログラムでは、TRANSACTION 句は記述し
なくてもかまいません。つまり、TRANSACTION 句は、同時に複数のトランザクション
を使用するプログラムに限って必要になります。たとえば、次の SELECT 文は、T1 とい
うトランザクションとともに機能します。
EXEC SQL
SELECT TRANSACTION T1:
COUNT(*), MAX(EMP_NO), MIN(EMP_NO), SUM(SALARY)
INTO :counter, :maxno, :minno, :total_salary
FROM EMPLOYEE;
トランザクション処理と命名の詳細は、第 4 章「トランザクションの操作」を参照してく
ださい。
第 6 章 データ処理
6-23
SELECT に よ る デ ー タ の 取 り 出 し
INTO での変数の指定
単一行 SELECT(1 つのテーブルの 1 行、または 1 行の中の任意のデータの取り出し)で
は、取り出されたデータは変数に格納されます。この変数は、SELECT 文の INTO 句を
使って指定します。この INTO 句は、列名のすぐ後に記述しなければなりません。また、変
数の先頭にはコロン(:)を付加するとともに、列名が複数のときは、各変数をカンマで区
切ります。
INTO 句で指定する変数は、事前に宣言しておかなくてはなりません。この宣言により、変
数を使用できるようになります。また、変数の数、順番、データ型は、SELECT で指定した
列の数、順番、データ型と対応するものでなければなりません。数、順番、データ型が違っ
ている場合、オーバーフローやデータ変換エラーが発生します。
たとえば、次の C 言語のコードでは、セクション宣言で、lname、fname、salary という 3
つの変数が宣言されています。このうち、lname と fname は文字列として、salary は 32
ビット整数として宣言されています。SELECT 文では、データの取り出し対象となる列が
3 つ指定されています。また、INTO 句では、変数が 3 つ指定されており、この 3 つの変
数に列のデータがそれぞれ格納されることになります。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
long salary;
char lname[20], fname[15];
EXEC SQL
END DECLARE SECTION;
. . .
EXEC SQL
SELECT LAST_NAME, FIRST_NAME, SALARY
INTO :lanem, :fname, :salary
FROM EMPLOYEE
WHERE LNAME = 'Smith';
. . .
メモ
複数行を取り出す場合は、INTO 句は SELECT 文ではなく FETCH 文とともに使用しま
す。FETCH 文における INTO 句の詳細は、6-38 ページの「カーソルによる行の取り出し」
を参照してください。
FROM でのテーブルの指定
SELECT 文には、必ず FROM 句を記述しなければなりません。この FROM 句で指定した
テーブル(またはビューや選択プロシージャ)から、データが取り出されることになりま
す。FROM の正規の構文は次のとおりです。
FROM table | view | procedure [alias] [, table | view | procedure
[alias] ...]
FROM 句には、テーブル、ビュー、選択プロシージャのいずれかの名前を少なくとも 1 つ
は記述しなければなりません。また、複数のテーブル(またはビュー、選択プロシージャ)
からデータを取り出す場合は、各テーブル名をカンマで区切って一覧します。また、エイ
リアスを使用する場合は、テーブルごとにエイリアスを付加します。選択プロシージャの
6-24 埋 め 込 み S Q L ガ イ ド
SELECT に よ る デ ー タ の 取 り 出 し
詳細は、第 10 章「ストアドプロシージャ」を参照してください。
1 つのテーブルまたはビューの指定
次の例は、FROM 句で 1 つのテーブルを指定する場合の記述です。ここでは、EMPLOYEE
テーブルを指定しており、このテーブルからデータが取り出されることになります。
EXEC SQL
SELECT LAST_NAME, FIRST_NAME, SALARY
INTO :lanem, :fname, :salary
FROM EMPLOYEE
WHERE LNAME = 'Smith';
また、次は、同じ INTO 句を使用して、テーブルではなくビュー(または選択プロシー
ジャ)をデータの取り出し元として指定している例です。この文では、MVIEW という選
択プロシージャがデータの取り出し元となっています。MVIEW には姓名が “M” で始ま
る名前のマネージャがすべて記録されています。また、WHERE 句で DEPT_NO 列のデー
タ(部署の番号)が 430 に限定されており、この結果、1 行だけが取り出されます。
EXEC SQL
SELECT DEPT_NO, LAST_NAME, FIRST_NAME, SALARY
INTO :lname, :fname, :salary
FROM MVIEW
WHERE DEPT_NO = 430;
選択プロシージャの詳細は、第 10 章「ストアドプロシージャ」を参照してください。
複数のテーブルの指定
複数のテーブル(またはビューや選択プロシージャ)からデータを取り出す場合は、その
名前を FROM 句に指定します。名前はそれぞれカンマで区切ります。
なお、データの取り出し元のテーブルが複数の場合、次の状況によって記述の方法が異な
ります。
1
2
同じ名前の列が、他のテーブルにまったくない場合
同じ名前の列が、他のテーブルにもある場合
最初のケースでは、SELECT 句には、通常どおり列名を記述します。たとえば、次のクエ
リーは、DEPARTMENT と EMPLOYEE という 2 つのテーブルからデータを取り出す例
です。
EXEC SQL
SELECT DEPARTMENT, DEPT_NO, LAST_NAME, FIRST_NAME, EMP_NO
INTO :dept_name, :dept_no, :lname, :fname, :empno
FROM DEPARTMENT, EMPLOYEE
WHERE DEPT_NO = 'Publications' AND MNGR_NO = EMP_NO;
一方、2 番めのケースでは、同じ名前の列が複数のテーブルにあるため、データを取り出す
列を特定することが必要になります。特定するには、テーブル名を先頭に付加し、ピリオ
ドを打った後に列名を続けます。たとえば、次は、DEPARTMENT と EMPLOYEE の両方
のテーブルに EMP_NO という列がある例です。
EXEC SQL
第 6 章 データ処理
6-25
SELECT に よ る デ ー タ の 取 り 出 し
SELECT DEPARTMENT, DEPT_NO, LAST_NAME, FIRST_NAME,
EMLOYEE.EMP_NO
INTO :dept_name, :dept_no, :lname, :fname, :empno
FROM DEPARTMENT, EMPLOYEE
WHERE DEPT_NO = 'Publications' AND
DEPARTMENT.EMP_NO = EMPLOYEE.EMP_NO;
SELECT 句の詳細は、6-21 ページの「SELECT での取り出し列の指定」を参照してくだ
さい。
重要
結合を行うクエリーでは、列名は、相関名を使って指定することができます。相関名、つ
まりエイリアスとは、テーブルに割り当てられた略語で、割り当ては FROM 句で行いま
す。割り当て後は、他の SELECT 文中で、そのエイリアスを使って列の指定が可能になり
ます。なお、結合を行わなくても、通常のクエリーで相関名を割り当てて使用することで、
複雑な記述を簡略化することができます。
相関名の宣言と使い方
テーブル名を示す一時変数を相関名(correlation name)またはエイリアス(alias)と言い
ます。相関名で使用できる文字は、英数字、ドル記号($)、下線(_)で、長さは最長で 31
字までとなっています。なお、相関名は必ず、アルファベットで始めなければなりません。
テーブルを示す相関名を使用することで、長いクエリーでも記述を簡略することができま
す。結合を行う場合は、この相関名を必ず使用しなければなりません。また、複雑なクエ
リーでテーブルを表すのにも利用できます。
相関名は、FROM 句でテーブル名の右に記述しておくと、そのテーブルと関連付けられま
す。以後の文で、その相関名を使って列の指定が可能です。たとえば、それぞれ、
DEPARTMENT テーブルに DEPT、EMPLOYEE テーブルに EMP という相関名を付ける
場合、次のように FROM 句を記述します。
FROM DEPARTMENT DEPT, EMPLOYEE EMP
上記のように、FROM 句で相関名を宣言した後は、SELECT 文でその相関名を使って列を
指定できます。たとえば、次は、検索条件として DEPT と EMP の 2 つの相関名を使って
列を指定しているクエリーの例です。
EXEC SQL
SELECT DEPARTMENT, DEPT_NO, LAST_NAME, FIRST_NAME,
EMLOYEE.EMP_NO
INTO :dept_name, :dept_no, :lname, :fname, :empno
FROM DEPARTMENT DEPT, EMPLOYEE EMP
WHERE DEPT_NO = 'Publications' AND DEPT.EMP_NO = EMP.EMP_NO;
SELECT 句の詳細は、6-21 ページの「SELECT での取り出し列の指定」を参照してくだ
さい。
WHERE での行の絞り込み
クエリーでは、WHERE 句を使用して、行の中に置かれている(または、置かれていない)
データを指定します。これで、そのデータを格納している行が取り出されることになります。
6-26 埋 め 込 み S Q L ガ イ ド
SELECT に よ る デ ー タ の 取 り 出 し
単一行 SELECT、つまり、1 行が取り出されるクエリーでは、WHERE 句は必ず記述しな
ければなりません。ただし、FROM 句に選択プロシージャが記述されており、その選択プ
ロシージャで返されるのが 1 行の場合は、WHERE 句は必要ありません。
また、SELECT 文が DECLARE CURSOR 文の中に記述されている場合、WHERE 句は
記述しても省略してもかまいません。WHERE 句を省略したときは、テーブルの全行が取
り出されます。一方、テーブルの行のうち必要な行だけを取り出すときは、WHERE 句を
使用しなければなりません。
WHERE 句の基本構文は次のとおりです。
WHERE <search_condition>
たとえば、次の簡単な WHERE 句の例は、DEPARTMENT 列に "Publications" というデー
タがあるかどうかを検索します。
WHERE DEPARTMENT = 'Publications'
検索条件の基本
WHERE 句では、クエリーでデータを検索するための条件を指定します。このため、
WHERE 句に記述した条件を一般に検索条件と呼んでいます。検索条件を指定しておく
と、テーブルの全行に対して、その条件に適合する行がないかどうかの検索が行われます。
この後、条件に適合した行が見つかると、その行が取り出しの該当行として認識されます。
行に対して検索条件による検査が行われた場合、次の 3 つの値のうち、いずれかが返され
ます。
• True:行が WHERE 句で指定されている検索条件に適合した場合、TRUE(真)が返され
ます。
• False:行が WHERE 句で指定されている検索条件に適合しなかった場合、FALSE(偽)
が返されます。
• Unknown:検査が行われた列の値が不定だった場合、UNKNOWN(不定)が返されます。
これは、値が NULL のため、評価不可能だったことを示します。
検索条件による検索では一般に、その条件がかなり複雑であっても、ほとんどの場合、
TRUE(真)または FALSE(偽)のどちらかとして評価されます。このように、TRUE
(真)または FALSE(偽)の評価を出す式を論理式と呼んでいます。WHERE 句の検索条
件も、この論理式です。
検索条件の構成要素
ある列の値と定数、または列の値同士で比較を行う、というのが検索条件の最も基本的な
形です。たとえば、次は、DEPARTMENT という列の値と "Publications" という定数を比
較し、この条件に適合する行がないかどうかを検査する WHERE 句の例です。
WHERE DEPARTMENT = 'Publications'
上記の検索条件は、列名、比較演算子(等号)、定数の 3 つの要素で構成されています。な
お、この検索条件は簡単な例で、通常はこれよりも複雑になります。つまり、構成要素の数
も増え、簡単な検索条件がいくつも組み合わされた形になっています。次の表は、検索条
件に使用できる式の要素を示しています。
第 6 章 データ処理
6-27
SELECT に よ る デ ー タ の 取 り 出 し
表 6.13 WHERE 句の検索条件の要素
要素
説明
列名
FROM 句で指定されているテーブル中の列名で、この列に対して値の
検索や比較が行われます。
ホスト言語変数
プログラムで使用されている変数で、格納されている値は変更が可能
です。SELECT で記述する場合、変数には、先頭にコロン(:)を付け
なければなりません。
定数
ハードコードされた数値または引用符で囲まれた文字列です。たとえ
ば、507 や “Tokyo” が該当します。
連結演算子
|| です。文字列を結合する際に使用します。
算術演算子
+、–、*、/ です。検索条件の値の計算や評価に使用されます。
論理演算子
NOT、AND、OR の 3 つのキーワードがあり、簡単な検索条件で使用
します。また、論理演算子を複数使用して、簡単な検索条件をいくつ
か組み合わせて複雑な検索条件を作成することもできます。論理演算
子では、通常、結果が TRUE(真)または FALSE(偽)で返されます。
比較演算子
<、>、<=、>=、=、<> で、演算子を挟んで左の値と右の値との比較
が行われます。比較演算子では、結果は TRUE(真)または FALSE
(偽)で返されます。
こ の ほ か、特 殊 な 比 較 演 算 子 と し て、ALL、ANY、BETWEEN、
CONTAINING、EXISTS、IN、IS、LIKE、NULL、SINGULAR、SOME、
STARTING WITH があります。これらの比較演算子では、結果は
TRUE(真)、FALSE(偽)、UNKNOWN(不定)のいずれかで返さ
れます。
COLLATE 句
CHAR または VARCHAR の値の比較では、COLLATE 句が必要にな
ることがあります。この句を使用して、比較の際の文字の照合順序を
設定できます。
ストアドプロシージャ
再利用可能な SQL 文のブロックです。ストアドプロシージャを使っ
てパラメータのやりとりが可能になります。ストアドプロシージャは、
データベースのメタデータとして格納されています。クエリーにおけ
るストアドプロシージャの詳細は、第 10 章「ストアドプロシージャ」
を参照してください。
サブクエリー
WHERE 句の中にネストされた SELECT 文を指し、メインの
SELECT 文により検索された行に対して比較を行い、比較による値を
返したり、計算を行ったりします。サブクエリーの詳細は、6-52 ペー
ジの「サブクエリー」を参照してください。
かっこ
かっこを使用して、式の複数の要素を相互の関連に基づいてグループ
化することができます。かっこで括った場合、その部分が優先的に処
理されるとともに、値が 1 つ生成されます。この値は、かっこを置い
た式の中で使用されます。また、かっこで囲んだ式は、式の中にネス
トすることができます。
簡単な検索条件を複数組み合わせることで、複雑な検索条件を作成することができます。た
とえば、次の WHERE 句は、列名が 2 つ、定数が 3 つ、比較演算子が 3 つ、かっこでグ
ループ化した式が 1 つ、という複雑な構成の検索条件の例です。この WHERE 句では、給
与が 60,000 ドルを超えていて 120,000 ドル未満の範囲である社員の行が取り出されます。
6-28 埋 め 込 み S Q L ガ イ ド
SELECT に よ る デ ー タ の 取 り 出 し
WHERE DEPARTMENT = 'Publications' AND
(SALARY > 60000 AND SALARY < 120000)
また、WHERE 句では、検索条件の中に、ネストされた SELECT 文(サブクエリー)を
記述することもあります。たとえば、次は、WHERE 句に集計関数である AVG() を使用し
ているクエリーの例です。このクエリーでは、給与を部署別で見た場合、その給与が全部
署の平均値より高い部署が取り出されます。
EXEC SQL
DECLARE WELL_PAID CURSOR FOR
SELECT DEPT_NO
INTO :wellpaid
FROM DEPARTMENT
WHERE SALARY > (SELECT AVG(SALARY) FROM DEPARTMENT);
SQL 式を使った検索条件の設定については、6-4 ページの「SQL 式」を参照してくださ
い。サブクエリーを使って検索条件を指定する方法の詳細は、6-52 ページの「サブクエ
リー」を参照してください。集計関数の詳細は、6-22 ページの「集合列のデータの取り出
し」を参照してください。
文字列の比較における照合順序
WHERE 句では、CHAR(n) または VARCHAR(n) の値(文字列)を相互に比較する際、
双方の値の照合順序が異なる場合は、照合順序を指定しなければなりません。
比較時に値に使用する照合順序を指定するには、値の後に COLLATE 句を入れます。たと
えば、次の埋め込みアプリケーションのコード内の WHERE 句では、比較演算子の左にあ
る値は、特定の照合順序で強制的に比較されます。
WHERE LNAME COLLATE FR_CA = :lname_search;
InterBase で用意されている照合順序の種類については、『データ定義ガイド』を参照して
ください。
ORDER BY による行のソート
クエリーが実行された場合、デフォルトで、検索で見つかった順番どおりにテーブルの行
が取り出されます。また、InterBase の内部では、テーブルの行は順不同で格納されている
ため、通常は取り出された行も順不同ということになります。InterBase には ORDER BY
句が用意されており、この句を使ってクエリーで行を取り出すと、同時に順番も整理(ソー
ト)することができます。ORDER BY 句はオプションで、SELECT 文の末尾に記述します。
ORDER BY 句では、必要に応じて列名を指定します。行は、この指定された列を基準に
してソートされることになります。ここで指定できる列名は、文の先頭部分の SELECT 文
で記述されているものに限られます。各列に対しては、ソート順として昇順(ASC、デフォ
ルト)または降順(DESC)を指定できます。ORDER BY の正規の構文は次のとおりです。
ORDER BY col [COLLATE collation] [ASC | DESC]
[,col [COLLATE collation] [ASC | DESC] ...];
たとえば、次のカーソル宣言では、取り出された行は LAST_NAME 列を基準にソートさ
れて出力されます。ここでは、ORDER BY 句で降順が指定されているため、姓名が Z で
始まる社員が最初に、A で始まる社員が最後に、という順番で出力されます。
EXEC SQL
第 6 章 データ処理
6-29
SELECT に よ る デ ー タ の 取 り 出 し
DECLARE PHONE_LIST CURSOR FOR
SELECT LAST_NAME, FIRST_NAME, PHONE_EXT
FROM EMPLOYEE
WHERE PHONE_EXT IS NOT NULL
ORDER BY LAST_NAME DESC, FIRST_NAME;
ORDER BY 句に複数の列を指定する
ORDER BY 句で列名を複数記述した場合、まず行は、最初の列を基準にソートされます。
この後、最初の列の値として同じ値を持つ行が複数あった場合、さらに 2 番めに指定した
列を基準に各行がソートされます。3 番め、4 番めの列を指定している場合も、同じ要領で
ソートが行われます。また、列名にはそれぞれ個別にソート順を指定できます。
重要
マルチカラムのソートでは、指定されたソート順は、それ以降、他のソート順が指定され
るまでのすべての列にも適用されます。これは前の例でも示されています。この属性は「ス
ティッキー」ソート順と呼ばれることもあります。前の例では、FIRST_NAME にソート
順が指定されていないので、この列のソート順は昇順になります。FIRST_NAME を降順
でソートする場合は、次の例のように明示的に DESC の指定を行います。
EXEC SQL
DECLARE PHONE_LIST CURSOR FOR
SELECT LAST_NAME, FIRST_NAME, PHONE_EXT
FROM EMPLOYEE
WHERE PHONE_EXT IS NOT NULL
ORDER BY LAST_NAME DESC, FIRST_NAME ASC;
ORDER BY 句における照合順序
SELECT 文で CHAR 列または VARCAHR 列を順序付ける場合、特に順序付けに使用さ
れる列が異なる照合順序を使用しているときは、順序付けの照合順序を指定する必要があ
ります。
ORDER BY 句で照合順序を指定するには、照合順序を指定する列名の右に COLLATE 句
と照合順序を記述します。これで、指定した照合順序がソートの際に使用されることにな
ります。たとえば、次の ORDER BY 句では、2 つの列に対して、それぞれ別の照合順序を
指定しています。
. . .
ORDER BY LNAME COLLATE FR_CA, FNAME COLLATE FR_FR;
InterBase で用意されている照合順序の種類については、『データ定義ガイド』を参照して
ください。
GROUP BY による複数行のグループ化
オプションの GROUP BY 句を使用して、列の値が同じ行を一括してグループ化すると同時
に、なんらかの集計値を出力することができます。したがって、GROUP BY では、検索条件
に該当する行が逐次取り出されることはありません。GROUP BY の正規の構文は次のとおり
です。
GROUP BY col [COLLATE collation] [, col [COLLATE collation] ...]
6-30 埋 め 込 み S Q L ガ イ ド
SELECT に よ る デ ー タ の 取 り 出 し
たとえば、ここで、カーソル宣言を 2 つ考えてみます。1 つは、テーブルから社員全員の部
署と姓名を取り出し、部署と姓名を基準に各行を昇順でソートします。記述は次のように
なります。
EXEC SQL
DECLARE DEPT_EMP CURSOR FOR
SELECT DEPARTMENT, LAST_NAME, FIRST_NAME
FROM DEPARTMENT D, EMPLOYEE E
WHERE D.DEPT_NO = E.DEPT_NO
ORDER BY DEPARTMENT, LAST_NAME, FIRST_NAME;
もう 1 つは、集計関数と GROUP BY を使用して、集計値を出力するカーソル宣言です。
この処理は、グループ集合と呼ばれています。このクエリーでは、部署別の社員の平均給与
が返されます。つまり、GROUP BY 句により、DEPARTMENT 列(部署)の値が同じ行
が全部取り出された後、各部署について平均値が計算されます。また、ORDER BY 句も
記述されているため、行(部署と平均給与)が部署名を基準にアルファベット順にソート
されます。
EXEC SQL
DECLARE AVG_DEPT_SAL CURSOR FOR
SELECT DEPARTMENT, AVG(SALARY)
FROM DEPARTMENT D, EMPLOYEE E
WHERE D.DEPT_NO = E.DEPT_NO
GROUP BY DEPARTMENT
ORDER BY DEPARTMENT;
GROUP BY 句での照合順序
SELECT 文で CHAR 列または VARCAHR 列をグループ分けする場合、特に、グループ
分けに使用される列が異なる照合順序を使用しているならば、グループ分けの照合順序を
指定する必要があります。
GROUP BY 句で列のグループ分けに使用する照合順序を指定するには、列名の後に
COLLATE 句を入れます。たとえば、次の GROUP BY 句では、2 つの列の照合順序が指
定されています。
. . .
GROUP BY LNAME COLLATE FR_CA, FNAME COLLATE FR_CA;
InterBase で用意されている照合順序の種類については、『データ定義ガイド』を参照して
ください。
GROUP BY を使用する場合の注意
GROUP BY を使用する場合、次の点に注意が必要です。
• GROUP BY 句で指定できる列名は、SELECT 句で指定されているものに限られます。
• GROUP BY 句では、算術関数や集計関数、ユーザー定義関数によって値が返されるよう
な列は指定できません。
• GROUP BY は、次のような SELECT 文では使用できません。
• INTO 句が記述されている場合(単一行 SELECT)
第 6 章 データ処理
6-31
SELECT に よ る デ ー タ の 取 り 出 し
• サブクエリーが記述され、その FROM 句でビューが指定されており、かつ GROUP
BY 句または HAVING 句が記述されている場合
• クエリー(サブクエリーを含む)では、GROUP BY 句は、SELECT 句 1 つにつき 1 つし
か使用できません。
HAVING による複数行のグループ化
SELECT 句では、WHERE 句を使って取り出す行の数を絞り込むことができます。同じよ
うに、HAVING 句を使用して、GROUP BY 句で取り出された行の数を限定することがで
きます。HAVING の構文は次のとおりです。
HAVING <search_condition>
HAVING では検索条件を記述でき、記述方法は WHERE 句の場合とほぼ同じです。ただ
し、次のような制限があります。
• HAVING 句の検索条件は、通常、SELECT 句で指定されている集計関数を基本に記述しま
す。
• HAVING 句でサブクエリーを記述する場合、その FROM 句では、メインのクエリーです
でに指定されているテーブルまたはビューの名前は指定できません。
• HAVING 句では、相関的なサブクエリーは記述できません。
たとえば、次は、会社の部署別に社員の平均給与を計算するカーソル宣言です。ここでは、
GROUP BY 句により、部署別の平均給与が計算されます。また、HAVING 句で、部署の
平均給与のうち 60000 を超えるものだけが取り出され、続いて、ORDER BY 句により、
行(部署と平均給与)が部署名を基準にアルファベット順にソートされて出力されます。
EXEC SQL
DECLARE SIXTY_THOU CURSOR FOR
SELECT DEPARTMENT, AVG(SALARY)
FROM DEPARTMENT D, EMPLOYEE E
WHERE D.DEPT_NO = E.DEPT_NO
GROUP BY DEPARTMENT
HAVING AVG(SALARY) > 60000
ORDER BY DEPARTMENT;
メモ
HAVING は、必ずしも GROUP BY と併用する必要はありません。HAVING を GROUP
BY なしで使った場合、SELECT により取り出された行はグループ化されません。したがっ
て、SELECT 句で列に対して集計関数を使用している場合、全行が計算の対象となります。
検索条件の詳細は、6-27 ページの「WHERE での行の絞り込み」を参照してください。サ
ブクエリーの詳細は、6-52 ページの「サブクエリー」を参照してください。
ROWS による結果セットの限定
ROWS 句を使用すると、行数、行の範囲、または行の割合を指定して、結果セットの一部
を取り出すことができます。また、n 行ごとに結果を取得したり、WITH TIES キーワード
を使って重複行を取得することもできます。ROWS は、ORDER BY 句と組み合わされて
最もよく使用されます。
6-32 埋 め 込 み S Q L ガ イ ド
SELECT に よ る デ ー タ の 取 り 出 し
ROWS 句の構文は次のとおりです。
ROWS <value> [TO <upper_value>] [BY <step_value>][PERCENT][WITH TIES]
ROWS 句は、テーブル式の結果セットから、一部の行だけを取り出します。この機能は、
結果を連続したチャンクで返す必要がある場合に便利です。たとえば、Web 開発者が Web
サーバーからクライアントのブラウザに大きな結果セットを小分けして送信する必要があ
る場合に使用できます。このような Web アプリケーションは、データベースサーバーとス
テートレスにやりとりするため、カーソルを使って結果セット全体を少しずつスクロール
したり、結果セット全体をクライアントのデータセットキャッシュにダウンロードするこ
とはできません。そのかわり、ROWS 句を使用して、結果セット全体から返される行の番
号を指定しながら、同じ SQL クエリーをデータベースサーバーに繰り返し送信します。
ROWS 句にはいくつかのオプションの要素があり、さまざまな結果を生成できます。表
6.14 にそれらのオプションを示します。
表 6.14 ROWS 句の形式
式
返される情報
ROWS n
結果セットの最初の n 行を返す。PERCENT とともに使用する場合は、
n パーセントを返す。
TOWS m TO n
m ~ n 行め(両端を含む)を返す。または、m ~ n パーセントの行を
返す。
ROWS n BY p
最初の n 行内の p 行ごとの行を返す。
ROWS m TO n BY p
m ~ n 行め内の p 行ごとの行を返す。
ROWS n PERCENT
• 結果セットの最初の n パーセントを返す。
• この例の「ROWS n」の部分には、任意の正しい「ROWS」構文
を置くことができる。PERCENT は、ROWS で指定されたすべ
ての値に適用される。
ORDER BY …
ROWS n WITH
TIES
• WITH TIES は、並べ替えられたシーケンス内の最後の値が結果
セットの後続の行内の値と同じ場合に、余分な重複行を返す。
ORDER BY と組み合わせて使用する必要がある。
• 行数を指定した場合、TIES で返される重複行は、単一行としてカ
ウントされる。
• この例の「ROWS n」部分には、任意の正しい「ROWS」構文を
置くことができる。
UNION によるテーブルの付加
場合によっては、データベース中に、構造が同じか、または列のデータが類似しているテー
ブルが複数収められていることがあります。このように、同じような構造を持ったテーブル
が複数ある場合は、各テーブルを結合して 1 つの結果テーブルを作成することができます。
この結果テーブルは、各テーブルの有効行をもとに作成された射影と呼ぶことができます。
テーブルの結合には UNION 句を使用します。この UNION 句により、各テーブルの全行
がまとめて取り出され、相互に結合されます。なお、重複データがあった場合には自動的
に排除され、1 つだけに絞られます。
テーブルの UNION は、データを集合する場合によく使用されます。
第 6 章 データ処理
6-33
SELECT に よ る デ ー タ の 取 り 出 し
UNION の構文は次のとおりです。
UNION SELECT col [, col ...]| * FROM <tableref> [, <tableref> ...]
たとえば、CITIES、COUNTRIES、NATIONAL_PARKS という 3 つのテーブルに、いず
れも都市名が格納されていたとします。また、トリガーは設定されておらず、したがって別
のテーブルに対して同じデータの自動入力は行われていないものとします。この場合、次
のように UNION を使用して、3 つのテーブルの都市名をすべて 1 つのテーブルにまとめ
ることができます。
EXEC SQL
DECLARE ALLCITIES CURSOR FOR
SELECT CIT.CITY FROM CITIES CIT
UNION SELECT COU.CAPITAL FROM COUNTRIES COU
UNION SELECT N.PARKCITY FROM NATIONAL_PARKS N;
ヒント
場合によっては、結合するテーブルがまったく同じ構造、つまり、列の数や名前、データ
型が同じで、各列の値も類似していることがあります。このような場合、UNION の
SELECT 句で列を指定するかわりにアスタリスク(*)を記述します。これで、列が全部
指定されます。
PLAN によるクエリープランの指定
InterBase では、SELECT 文は、クエリー最適化機能(query optimizer)と呼ばれる内部
アルゴリズムによって処理されます。この場合、クエリー最適化機能により、データの取
り出しに最も効果的な方法が自動的に採用されます。したがって、このクエリー最適化機能
にクエリーの方法を任せることで、最高速のデータ取り出しが可能になります。しかし、場
合によっては、効率の悪い方法が選択されることもあります。たとえば、データベースの使
用状況によっては、テーブルの行数がかなり多くなることもあります。また、テーブルの
インデックス付きの列にいくつも重複行が挿入されたり、多数の重複行が削除されたりし
て、インデックスの選択能力が低下するといったことも起こります。このような場合、最
適化機能により効率の低いクエリーの方法が選択されてしまいます。
このような場合は、オプションの PLAN 句を使用して、データ取り出しのための独自のク
エリーの方法を指定できます。このクエリーの方法をクエリープランと呼びます。PLAN
では、主にインデックス関連の操作を行いクエリープランを作成します。つまり、使用する
インデックスの指定、複数のインデックスの結合、アクセス方法の指定といった作業を行っ
て、クエリープランを構築することになります。
クエリープランを指定するための PLAN の構文は次のようになります。
PLAN <plan_expr>
<plan_expr> =
[JOIN | [SORT] MERGE] (<plan_item> | <plan_expr>
[, <plan_item> | <plan_expr> ...])
<plan_item> = {table | alias}
NATURAL | INDEX ( <index> [, <index> ...]) | ORDER <index>
PLAN 構文では、1 つのテーブルを指定できるほか、複数のテーブルを結合することもで
きます。また、かっこを使って式(<plan_expr>)をネストし、複雑な式を記述することも
できます。
6-34 埋 め 込 み S Q L ガ イ ド
1 行の取り出し
PLAN で複数のテーブルを結合した場合、取り出しの際は各テーブルのデータが結合され
るため、処理が高速化されます。また、インデックスを指定したときは、そのインデックス
を使って結合が行われます。テーブルを結合する場合、JOIN キーワードを使用します。結
合するテーブルのデータにインデックスが設定されていないときは、JOIN のかわりに
SORT MERGE を指定して取り出し処理を高速化できます。
plan_item は、検索対象のデータが格納されているテーブル名を表します。クエリーで、同
じテーブルを複数回使用する場合は、PLAN 句ではエイリアスを使ってテーブルを指定し
なければなりません。また、plan_item では、行に対するアクセス方法を指定します。指定
できるアクセス方法と処理内容は、次の 3 種類です。
• NATURAL:テーブルの行に対して順次アクセスが行われます。デフォルトのアクセス方
法で、インデックスが付けられていない場合、この方法以外では指定できません。
• INDEX:指定したインデックスを介して行へのアクセスが行われます。使用するインデッ
クスは、INDEX の右にすべて列挙しておかなければなりません。なお、INDEX にテーブ
ルのインデックスが全部指定され、その PLAN 句の後に論理演算子または連結演算子によ
る処理が記述されているときは、その処理ではインデックスは一切使用されません。また、
INDEX で指定したインデックスが使用不能のときは、エラーが報告されます。
• ORDER:インデックスの名前を併記しておくと、そのインデックスに設定されているソー
ト順にしたがって行がソートされます。
1 行の取り出し
テーブルから 1 行または行の一部を取り出す処理を単一行 SELECT(singleton select)と
呼びます。単一行 SELECT では、SELECT 文は次のようになります。この構文は、ユニー
クインデックスが設定されている列のデータを取り出す場合、また、COUNT() や AVG()
を使って集合値を取り出す場合も同じです。
SELECT <col> [, <col> ...]
INTO :variable [, :variable ...]
FROM table
WHERE <search_condition>;
上の文で、INTO 句は必須で、ここには変数を指定します。取り出されたデータはこの変
数に格納され、プログラムで使用されることになります。変数の名前の前にはコロン(:)
を付けなければなりません。変数は、データを取り出す列(SELECT 句で指定した列)の
数だけ必要です。また、データは、SELECT 句で指定した列の順番で取り出されるため、
INTO の変数も同じ順番で記述する必要があります。
単一行 SELECT の場合、WHERE 句では、1 行が取り出されるように検索条件を記述しな
ければなりません。検索条件で取り出される行が複数のときは、その SELECT 句による処
理は実行されません。
重要
ユーザーがテーブルからデータを取り出すには、そのユーザーに、データが格納されてい
るテーブルの SELECT 特権が与えられていることが条件となります。また、ユーザーがス
トアドプロシージャを使用する場合、そのストアドプロシージャに、データを取り出すテー
ブルの SELECT 特権が与えられていることが必要です。
第 6 章 データ処理
6-35
複数行の取り出し
次の例の SELECT は、DEPARTMENT テーブルから Publications 部の情報を取り出しま
す。
EXEC SQL
SELECT DEPARTMENT, DEPT_NO, HEAD_DEPT, BUDGET, LOCATION, PHONE_NO
INTO :deptname, :dept_no, :manager, :budget, :location, :phone
FROM DEPARTMENT
WHERE DEPARTMENT = 'Publications';
上記のクエリーでは、取り出された行のうち、DEPARTMENT 列のデータが変数 deptname
に、DEPT_NO 列のデータが変数 dept_no に、HEAD_DEPT 列のデータが変数 manager
に、順に格納されます。
複数行の取り出し
一般にほとんどのクエリーでは、複数行を取り出すための検索条件を指定します。たとえ
ば、給与が 60000 ドルを超える社員をすべて取り出すといったクエリーがあります。
変数には 1 つのデータしか格納できないことから、複数行を取り出すクエリーではメモリ
上に一時的なテーブルが作成され、このテーブルに行が格納されます。このテーブルを結
果テーブルと呼びます。結果テーブルに格納された行は、その後一括して、格納された順
番で抽出および処理が行われることになります。なお、SQL では、次に処理する結果テー
ブル上の行に対して自動的にポインタが作成され、このポインタにより行の管理が行われ
ます。このポインタはカーソルと呼ばれます。
重要
DSQL では、複数行を取り出す場合のクエリーの記述方法が SQL とは多少異なります。
DSQL における複数行の選択の詳細は、6-44 ページの「DSQL での複数行の取り出し」を
参照してください。
上記のように、複数行を取り出すクエリーでは、行は結果テーブルに暫定的に格納され、
カーソルにより行が処理されることになります。したがって、この種のクエリーでは、次
のような文を順に記述しなければなりません。
1 DECLARE CURSOR:カーソル名を宣言し、実行するクエリーを指定します。
2 OPEN:クエリーを実行します。結果テーブルが作成され、カーソルがテーブルの先頭
に配置されます。
3 FETCH:この FETCH により、結果テーブルの行が 1 行ずつ取り出され、変数に格納
されます。
4 CLOSE:すべての行が取り出されたら、CLOSE によってシステムリソースが解放さ
れます。
重要
ユーザーによるデータの取り出しでは、そのユーザーに、データが格納されているテーブ
ルの SELECT 特権が与えられていることが条件となります。また、ユーザーがストアドプ
ロシージャを使用する場合、そのストアドプロシージャに、データを取り出すテーブルの
SELECT 特権が与えられていることが必要です。
6-36 埋 め 込 み S Q L ガ イ ド
複数行の取り出し
カーソルの宣言
DECLARE CURSOR 文では、カーソルの宣言を行います。また、SELECT 句を使用して、
最終的に取り出す行を指定します。
な お、DECLARE CURSOR は情報文で、クエリーの実行とは無関係です。つまり、
DECLARE CURSOR に記述された情報に基づいてシステムリソースの準備が行われ、後
続の OPEN でカーソルが開かれると、そのシステムリソースが割り当てられます。このよ
うに、DECLARE CURSOR はクエリーの実行とは直接関係ないため、SQLCODE が割り
当てられることはありません。
DECLARE CURSOR の構文は次のとおりです。
DECLARE cursorname CURSOR FOR
SELECT <col> [, <col> ...]
FROM table [, <table> ...]
WHERE <search_condition>
[GROUP BY col [, col ...]]
[HAVING <search_condition>]
[ORDER BY col [ASC | DESC] [, col ...] [ASC | DESC]
| FOR UPDATE OF col [, col ...]];
上の例で、cursorname はカーソル名です。この名前は、後続の OPEN、FETCH、CLOSE
の各文で有効なカーソルを指定するときに使用できます。
次 の 例 を 除 い て、DECLARE CURSOR 内の SELECT 文の記述方法は、DECLARE
CURSOR がない場合の SELECT と同じです。
• DECLARE CURSOR 中の SELECT では、INTO 句は記述できません。
• DECLARE CURSOR 中の SELECT では、ORDER BY 句と FOR UPDATE 句のどちら
か 1 つしか記述できません。
次は、DECLARE CURSOR によるカーソル宣言の例です。
EXEC SQL
DECLARE TO_BE_HIRED CURSOR FOR
SELECT D.DEPARTMENT, D.LOCATION, P.DEPARTMENT
FROM DEPARTMENT D, DEPARTMENT P
WHERE D.MNGR_NO IS NULL
AND D.HEAD_DEPT = P.DEPT_NO;
カーソルを使った更新
アプ リ ケー シ ョン で は、通常、データの取り出しの後に更新を行います。この場合、
DECLARE CURSOR でオプションの FOR UPDATE 句を記述することで、取り出した
データを更新可能な状態にしておくことができます。FOR UPDATE の右に列名を指定す
ると、この列のデータが更新可能な状態になります。次は、FOR UPDATE 句を記述した
カーソル宣言の例です。
EXEC SQL
DECLARE H CURSOR FOR
第 6 章 データ処理
6-37
複数行の取り出し
SELECT CUST_NO
FROM CUSTOMER
WHERE ON_HOLD = '*'
FOR UPDATE OF ON_HOLD;
FOR UPDATE 句 は 記 述を省略することも可能で、この場合、取り出された行の列
(SELECT で指定した列)すべてが更新可能になります。たとえば、次のクエリーでは、2
つの列のデータが両方とも更新可能になります。
EXEC SQL
DECLARE H CURSOR FOR
SELECT CUST_NAME CUST_NO
FROM CUSTOMER
WHERE ON_HOLD = '*';
カーソルによる列の更新の詳細は、6-62 ページの「複数行の更新」を参照してください。
カーソルのオープン
カーソル宣言の後、OPEN 文を使ってカーソルを開くと、データの取り出しが可能になり
ます。OPEN では、カーソルが起動すると同時に結果テーブルが作成されます。この場合、
結果テーブルは、DECLARE CURSOR 文で指定されている検索条件をもとに作成されま
す。なお、結果テーブルに格納されている行をまとめて、カーソルのアクティブセットと呼
んでいます。
次の文は、DECLARE CURSOR で宣言済みの DEPT_EMP というカーソルを開く場合の
記述です。
EXEC SQL
OPEN DEPT_EMP;
OPEN 文でカーソルを開くと、カーソルは結果テーブルの最初の行の先頭に置かれます。
カーソルによる行の取り出し
カーソルを開くと、行の取り出しが可能な状態になります。行の取り出しには FETCH 文
を使用します。FETCH では、行が結果テーブルから一度に 1 行ずつ取り出されます。
FETCH の処理の順序は、次のようになります。
1
2
3
結果テーブルの最初の有効行が取り出されます。
取り出された行が、FETCH 文の INTO 句に記述された変数に格納されます。
結果テーブルの次の有効行の先頭にカーソルが置かれます。なお、カーソルがテーブル
の末尾に移動し、その後には取り出し対象の行がない場合は、SQLCODE に 100 が入
れられます。
FETCH 文の正規の構文は次のとおりです。
FETCH <cursorname> INTO :variable [[INDICATOR] :variable]
[, :variable [[INDICATOR] :variable>] ...];
6-38 埋 め 込 み S Q L ガ イ ド
複数行の取り出し
重要
FETCH は、DSQL で複数行の取り出しを行う場合にも使用できますが、使い方が異なり
ます。DSQL での複数行の取り出しの詳細は、6-46 ページの「DSQL カーソルによる複数
行の取り出し」を参照してください。
たとえば、次の文は、DEPT_EMP というカーソルを使って結果テーブルの行を取り出し、
列のデータをそれぞれ deptname、lname、fname という変数に格納する例です。
EXEC SQL
FETCH DEPT_EMP
INTO :deptname, :lname, :fname;
上記と同じ要領で、結果テーブルの行全部を順に取り出すこともできます。この場合、
FETCH 文をホスト言語の繰り返し文の中に記述します。たとえば、次の C のコードでは、
DEPT_EMP カーソルによって取り出される行がすべて表示されます。
. . .
EXEC SQL
FETCH DEPT_EMP
INTO :deptname, :lname, :fname;
while (!SQLCODE)
{
printf("%s %s works in the %s department.¥n", fname,
lname, deptname);
EXEC SQL
FETCH DEPT_EMP
INTO :deptname, :lname, :fname;
}
EXEC SQL
CLOSE DEPT_EMP;
. . .
FETCH 文ではいずれも、アクティブセット(結果テーブルの各行)が終了したかどうかを
判定しなければなりません。たとえば、上の例では SQLCODE を使用しています。この
SQLCODE が 0 の間は while ループの処理が続きます。一方、結果テーブルで取り出す行
がなくなった時点で SQLCODE に 100 が設定され、while ループの処理が終了します。な
お、SQLCODE の値がマイナスのときは、なんらかのエラーが発生したことを示します。
INDICATOR によるインジケータ変数の設定
列に NOT NULL または UNIQUE の整合性制約が設定されている場合は別ですが、テー
ブルの列によっては値が NULL のことがあります。この NULL は、列の値が未入力の場
合、InterBase によりフラグとして自動的に設定された値です(列の属性が NULL に設定さ
れている場合)。
列の値が NULL であるかどうかは、INDICATOR キーワードを使って確認できます。この
INDICATOR は、INTO 句に指定した変数の右に記述します。また、INDICATOR の右に
は 16 ビット整数の変数を指定します。この 16 ビット整数の変数をインジケータ変数
(indicator variable)と呼び、取り出された値に応じて、フラグの状況(NULL であるかど
うか)を示す値がインジケータ変数に設定されます。インジケータ変数に設定される値は次
の 2 種類です。
• 取り出された値が NULL の場合、インジケータ変数は –1 に設定されます。
第 6 章 データ処理
6-39
複数行の取り出し
• 取り出された値が NULL 以外の場合、インジケータ変数は 0 に設定されます。
たとえば、次の C コードは、department、manager、missing_manager という 3 つの変数
を宣言し、2 つの列の値を department と manager の 2 つの変数に格納する例です。ここ
では、変数 manager に INDICATOR が設定されているので、変数 manager に対応する列
の値に応じて、インジケータ変数 missing_manager にフラグが設定されます。FETCH 文
の GETCITY は、宣言済みのカーソルの名前です。
. . .
char department[26];
char manager[36];
short missing_manager;
. . .
FETCH GETCITY INTO :department, :manager INDICATOR :missing_manager;
なお、INDICATOR キーワードは省略することもできます。次は、省略した例です。
FETCH GETCITY INTO :department, :manager :missing_manager;
さらに、列の値が格納される変数と NULL 値のフラグを含むインジケータ変数の間のス
ペースも省略できます。その場合、記述は次のようになります。
FETCH GETCITY INTO :department, :manager:missing_manager;
メモ
InterBase では、FETCH で記述した変数の数と DECLARE CURSOR の SELECT で記述
した列の数は一致しなければなりませんが、FETCH 文のインジケータ変数は例外でカウン
トされません。
カーソルによる行の再取り出し
FETCH による処理では、カーソルはアクティブセットの最初から最後まで一度しか移動し
ません。
したがって、同じ結果テーブルの行をもう一度取り出すときは、いったんカーソルを閉じ
て、OPEN 文を使ってカーソルを再度開かなければなりません。たとえば、DEPT_EMP
カーソルをいったん閉じて、再度開く場合、次のようになります。これで、同じ結果テー
ブルの先頭にカーソルが再設定されます。
EXEC SQL
CLOSE DEPT_EMP;
EXEC SQL
OPEN DEPT_EMP;
カーソルのクローズ
カーソルがアクティブセットの最後に移動したら、そのカーソルを閉じなければなりませ
ん。その結果、システムリソースが解放されます。カーソルを閉じるには CLOSE 文を使
用します。次の文は、DEPT_EMP カーソルを閉じる場合の記述例です。
EXEC SQL
CLOSE DEPT_EMP;
6-40 埋 め 込 み S Q L ガ イ ド
複数行の取り出し
なお、カーソルがアクティブセットの最後まで達したかどうかは、SQLCODE を使って検
証します。SQLCODE の値が 100 に設定された場合、取り出す行が存在しないと判定され
ます。
カーソルによる行の取り出しのプログラム例
次は、カーソルを宣言して開いた後、ループ処理によってアクティブセットでカーソルを
移動し、FETCH による値の取り出しと表示の各処理をまとめたプログラム例です。このプ
ログラムでは、処理がすべて終了するとカーソルが閉じます。また、エラーが発生したと
きもカーソルが閉じるように記述してあります。
#include <stdio.h>
EXEC SQL
BEGIN DECLARE SECTION;
char deptname[26];
char lname[16];
char fname[11];
EXEC SQL
END DECLARE SECTION;
main ()
{
EXEC SQL
WHENEVER SQLERROR GO TO abend;
EXEC SQL
DECLARE DEPT_EMP CURSOR FOR
SELECT DEPARTMENT, LAST_NAME, FIRST_NAME
FROM DEPARTMENT D, EMPLOYEE E
WHERE D.DEPT_NO = E.DEPT_NO
ORDER BY DEPARTMENT, LAST_NAME, FIRST_NAME;
EXEC SQL
OPEN DEPT_EMP;
EXEC SQL
FETCH DEPT_EMP
INTO :deptname, :lname, :fname;
while (!SQLCODE)
{
printf("%s %s works in the %s department.¥n",fname,
lname, deptname);
EXEC SQL
FETCH DEPT_EMP
INTO :deptname, :lname, :fname;
}
EXEC SQL
CLOSE DEPT_EMP;
exit();
第 6 章 データ処理
6-41
複数行の取り出し
abend:
if (SQLCODE)
{
isc_print_sqlerror();
EXEC SQL
ROLLBACK;
EXEC SQL
CLOSE_DEPT_EMP;
EXEC SQL
DISCONNECT ALL;
exit(1)
}
else
{
EXEC SQL
COMMIT;
EXEC SQL
DISCONNECT ALL;
exit()
}
}
NULL 値が格納されている行の取り出し
列に NOT NULL または UNIQUE の整合性制約が設定されている場合は別ですが、テー
ブルの列によっては値が NULL のことがあります。この NULL は、列の値が未入力の場
合、InterBase によりフラグとして自動的に設定された値です(列の属性が NULL に設定さ
れている場合)。
列の値として NULL が格納されている行は、WHERE 句に IS NULL を記述して検索でき
ます。たとえば、DEPARTMENT というテーブルの行のうち、BUDGET という列に値が
入力されていない行がいくつかあったとします。
つまり、会社の各部署を記録したテーブルで、まだ予算を入力していない部署があり、そ
の部署の予算の列(BUDGET)の値が NULL ということになります。この場合、次のカー
ソル宣言を使用して、予算の入力が終わっていない部署を取り出すことができます。また、
FOR UPDATE を使って更新の準備も行っています。
EXEC SQL
DECLARE NO_BUDGET CURSOR FOR
SELECT DEPARTMENT, BUDGET
FROM DEPARTMENT
WHERE BUDGET IS NULL
FOR UPDATE OF BUDGET;
メモ
列に NULL 値が入っているかどうかは、インジケータ変数を使って検証できます。インジ
ケータ変数の詳細は、6-40 ページの「INDICATOR によるインジケータ変数の設定」を
参照してください。
6-42 埋 め 込 み S Q L ガ イ ド
複数行の取り出し
直接クエリーでは、列の値が NULL の場合、その列のデータが数値のときには 0 が返され
ます。また、文字のときは空白が、日付のときには "17 November 1858" が返されます。た
とえば、次のカーソル宣言では、各部署の予算がすべて取り出されますが、そのうち NULL
値を持つ行については 0 が返されます。
EXEC SQL
DECLARE ALL_BUDGETS CURSOR FOR
SELECT DEPARTMENT, BUDGET
FROM DEPARTMENT
ORDER BY BUDGET DESCENDING;
NULL 値を扱う場合の注意
InterBase では、NULL は値のない値として認識されます。このため、NULL 値を扱うと
きは、次のような点に注意が必要です。
• NULL 値が格納された列を含む行のソートは、他の行のソート後に実行されます。
• 集合処理では、NULL 値は計算の対象から除外されます。ただし、COUNT(*) では集合の
対象となります。
• 否定処理を行う検索条件では、NULL 値は取り出されません。
• 結合の検索条件では、列の値として NULL が格納されている行は取り出されません。
NULL 値は、比較演算子を使って他の値と比較することができます。この場合、比較演算
子のどちらか一方の値が NULL のときは、UNKNOWN(不定)が返されます。
また、論理演算子(NOT、AND、OR)による比較では、戻り値は次のようになります。
• NOT では、左側と右側の検索条件の戻り値にかかわりなく、必ず UNKNOWN(不定)が
返されます。
• AND では、どちらかの検索条件のオペランドが FALSE(偽)以外のときは、UNKNOWN
(不定)が返されます。どちらかのオペランドが FALSE(偽)の場合は、FALSE(偽)が
返されます。
• OR では、どちらかの検索条件のオペランドが TRUE(真)以外のときは、UNKNOWN
(不定)が返されます。どちらかのオペランドが TRUE(真)のときは、TRUE(真)が返
されます。
入力がない場合の値として、NULL 以外の値をデフォルトとすることもできます。NULL
以外の値の定義については、『データ定義ガイド』を参照してください。
ビューによる行の取り出し
テーブルのかわりにビューを使って行の一部を取り出すときは、SELECT の FROM 句に
テ ー ブ ル で は な く ビ ュ ー の 名 前 を 記 述 し ま す。た と え ば、次 の カ ー ソ ル 宣 言 で
は、PHONE_VIEW というビューをもとに社員の電話番号のリストが出力されます。
EXEC SQL
DECLARE PHONE_LIST CURSOR FOR
SELECT FIRST_NAME, LAST_NAME, PHONE_EXT
FROM PHONE_VIEW
第 6 章 データ処理
6-43
DSQL での複数行の取り出し
WHERE EMPLOYEE.DEPT_NO = DEPARTMENT.DEPT_NO;
ビューを使って行を取り出す場合、そのビューは複数のテーブルをもとに作成されたもの
(CREATE VIEW で作成)でもかまいません。また、JOIN キーワードを使用して、テーブ
ルのかわりに複数のビューを結合することもできます。ビューの結合の詳細は、6-46 ペー
ジの「テーブルの結合」を参照してください。
DSQL での複数行の取り出し
DSQL では、実行時にユーザーがクエリーを入力することができます。このユーザーから
のクエリーをプログラム側で受け取るには、拡張 SQL デスクリプタエリア(XSQLDA)
を使用しなければなりません。クエリーの入出力は、XSQLDA を介して扱われることにな
ります。多少形は異なりますが、DSQL でも DECLARE CURSOR や OPEN、FETCH の
各文を利用できます。複数行を取り出すクエリーに対しては、XSQLDA と関連させなが
ら、このような文で処理を行うことになります。
DSQL でも、SQL と同様に、取り出した行を結果テーブルに順に格納し、カーソルを所定
の場所に置いて各行を処理するといった手順が必要です。DSQL では、次の手順でこのよ
うな処理を行います(カーソル宣言は省略)。
1 PREPARE:XSQLDA 構造体を用意します。ここには、クエリーの結果が保持されま
す。
2 DECLARE CURSOR:カーソル名を宣言し、実行するクエリーを指定します。
3 OPEN:クエリーを実行します。結果テーブルが作成され、カーソルがテーブルの先頭
に配置されます。
4 FETCH:テーブルから一度に 1 行ずつ取り出します。取り出された行は、後続のプロ
グラムで使用されます。
5 CLOSE:すべての行が取り出されたら、CLOSE によってシステムリソースが解放さ
れます。
DSQL カーソルの宣言方法、DSQL カーソルを開く方法、および DSQL カーソルによる行
の取り出し方法については、以降のトピックで説明します。XSQLDA 構造体の作成とデー
タ入力、および PREPARE による DAQL クエリーの準備の詳細は、第 13 章「動的 SQL」
を参照してください。カーソルを閉じる方法の詳細は、6-41 ページの「カーソルのクロー
ズ」を参照してください。
DSQL カーソルの宣言
DSQL では、ユーザーが入力した SELECT 文をもとにカーソル宣言が行われます。処理の
手順は次のようになります。
• ユーザーにクエリー(SELECT 文)の入力を求めます。
• ユーザーが入力したクエリーを変数に格納します。
• PREPARE 文を使用して、変数をもとに、クエリーと結果保持用の XSQLDA を用意しま
す。
6-44 埋 め 込 み S Q L ガ イ ド
DSQL での複数行の取り出し
• クエリーのエイリアスと関連付けて、カーソルを宣言します。
DSQL では、DECLARE CURSOR の正規の構文は次のようになります。
DECLARE cursorname CURSOR FOR queryname;
次は、DSQL のカーソル宣言の C のコード例です。まず、ユーザーの入力したクエリーを
保持するための文字列変数として querystring を宣言した後、gets でユーザーのクエリーを
querystring に格納しています。続いて、querystring をもとに PREPARE で QUERY とい
う名前のクエリーを用意し、最後に、C というカーソルを宣言しています。このカーソル
C は、クエリーの QUERY と関連付けています。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
char querystring [512];
XSQLDA *InputSqlda, *OutputSqlda;
EXEC SQL
END DECLARE SECTION;
. . .
printf("Enter query:"); /* ユーザーにクエリーの入力を求めます */
gets(querystring); /* 文字列を取得し、querystring に格納します */
. . .
EXEC SQL
PREPARE QUERY INTO OutputSqlda FROM :querystring;
. . .
EXEC SQL
DECLARE C CURSOR FOR QUERY;
XSQLDA 構造体の作成とデータ入力、および PREPARE による DSQL クエリーの準備の
詳細は、第 13 章「動的 SQL」を参照してください。
DSQL カーソルのオープン
カーソル宣言後、OPEN 文を使ってカーソルを開きます。セクション宣言で宣言済みの入
力用 XSQLDA の情報をもとに結果テーブルが作成され、データの取り出しが可能になり
ます。DSQL の OPEN の構文は、次のとおりです。
OPEN cursorname USING DESCRIPTOR sqldaname;
たとえば、C というカーソルを InputSqlda という XSQLDA を使って開く場合、次のよう
になります。
EXEC SQL
OPEN C USING DESCRIPTOR InputSqlda;
第 6 章 データ処理
6-45
テーブルの結合
DSQL カーソルによる複数行の取り出し
結果テーブルから行を取り出すには FETCH 文を使用します。FETCH によって、宣言済み
の出力用 XSQLDA(拡張 SQL デスクリプタエリア)の情報をもとに行が取り出されます。
DSQL の FETCH の構文は、次のとおりです。
FETCH cursorname USING DESCRIPTOR descriptorname;
たとえば、入力用と出力用の 2 つの XSQLDA 構造体を宣言し、出力用の XSQLDA 構造
体を使って FETCH 文で行を取り出す場合、C のコードは次のようになります。
. . .
XSQLDA *InputSqlda, *OutputSqlda;
. . .
EXEC SQL
FETCH C USING DESCRIPTOR OutputSqlda;
. . .
XSQLDA 構造体の作成とデータ入力、および PREPARE による DSQL クエリーの準備の
詳細は、第 13 章「動的 SQL」を参照してください。
テーブルの結合
結合(join)とは、1 つの SELECT 句を使用して、2 つ以上のテーブルからデータを取り
出す処理を言います。データの取り出し元となるテーブルは、JOIN キーワードを使って
FROM 句に指定します。FROM 句では、オプションで検索条件を記述し、取り出す行を限
定することもできます。さらに、WHERE 句で検索条件を使用して、行を絞り込むことが
できます。
SELECT 句に JOIN を記述した場合、その SELECT 句の情報に基づいてテーブルが作成
されます。このテーブルを結果テーブルと呼び、ここには、結合処理での結果が格納され
ます。このテーブルを、動的テーブルまたは仮想テーブルと呼ぶこともあります。
InterBase は、次の 2 種類の結合をサポートしています。
• 内部結合(inner join)は、結合の検索条件(ON 句の検索条件)にしたがって各テーブル
の行を比較し、行のうち結合の検索条件に適合するものだけを取り出します。内部結合は、
さらに次の 3 種類に分かれます。
• 同等結合(equi-join)は、各テーブルの列に共通する値、つまり等式を使って列の
値を比較し、行を取り出します。
• 比較結合(comparative join)または非同等結合(non-equi-join)は、正式に名前が
決まっているわけではありませんが、分類の都合上こう呼んでいます。同等結合と
違って、比較演算子で列を結び、各テーブルの行を取り出します。
• 自己結合(self-join)または再帰結合(reflexive join)は、1 つのテーブルの列を
使って値を比較します。
• 外部結合(outer join)は、結合の検索条件(ON 句の検索条件)にしたがって各テーブル
の行を比較し、行のうち結合の検索条件に適合するものだけを取り出します。その他に、
一方または両方のテーブルについて、結合の検索条件に適合しない行も取り出せます。
6-46 埋 め 込 み S Q L ガ イ ド
テーブルの結合
結合には上の 2 種類がありますが、通常は内部結合の方がよく使用されます。これは、デー
タの絞り込みにも優れ、双方のテーブルの関係をはっきりと把握できるためです。ただし、
外部結合も、結合の検索条件に該当しない行を参考にデータを確認できるという点では有
効です。
結合で使用する列の選択
ここでは、結合に使用する列を選択するための基準について説明します。結合に使用する列
は、少なくとも互いのデータ型に互換性があり、互いに内容が似ている必要があります。た
とえば CHAR 型の列を INTERGER 型の列に結合することはできません。一般的で安全な
基準として、あるテーブルの FOREIGN KEY 列を、これが参照する PRIMARY KEY 列
に結合するようにします。2 つのテーブル上の同一の列を結合する場合も多くあります。た
とえば、
「業務」テーブルと「従業員」テーブルがそれぞれ持つ job_code という列を結合
できます。
INTEGER、DECIMAL、NUMERIC、FLOAT の各データ型の列はいずれも値が数値です
ので、比較可能です。CHAR と VARCHAR などの文字列型は、文字列としか比較できま
せん。ただし、相手側の ASCII データが数値だけで構成されている場合、比較は不可能で
す。データ型が異なり比較できない場合は、CAST() 関数でデータ型を変換します。CAST()
の詳細は、6-17 ページの「CAST() によるデータ型の変換」を参照してください。
重要
結合処理では、いずれかの行の列に NULL 値が含まれている場合、その行は結果テーブル
に格納されません。ただし、外部結合では、NULL が含まれている行も結果テーブルに抽
出されます。
内部結合
InterBase では、内部結合を記述する方法が 2 種類あります。1 つは、従来の SQL アプリ
ケーションで結合を記述するときの方法で、これは、互換性や移植性を考慮したものです。
従来の SQL アプリケーションでは結合用のキーワードや記述方法が用意されていません
でした。このため、内部結合を記述する場合、SELECT 句の中の FROM 句を使用して、結
合するテーブルを個別に指定する必要があります。また、列は WHERE 句で比較していま
した。
たとえば、次は従来の SQL アプリケーションの結合処理の例です。ここでは、会社の部署
名、マネージャ(部長)番号、および該当する部署の従業員全員に対する給与の三分の一
以上を支給されているマネージャ(部長)の給与額が出力されます。
EXEC SQL
DECLARE BIG_SAL CURSOR FOR
SELECT D.DEPARTMENT, D.MNGR_NO, E.SALARY
FROM DEPARTMENT D, EMPLOYEE E
WHERE D.MNGR_NO = E.EMP_NO
AND E.SALARY*2 >= (SELECT SUM(S.SALARY) FROM EMPLOYEE S
WHERE D.DEPT_NO = S.DEPT_NO)
ORDER BY D.DEPARTMENT;
もう 1 つは、新たに採用されたもので、JOIN 構文を使って結合を明示的に記述する方法で
す。この記述方法は、SQL-92 に準拠しています。構文は次のとおりです。
第 6 章 データ処理
6-47
テーブルの結合
SELECT col [, col ...] | *
FROM <tablerefleft> [INNER] JOIN <tablerefright>
[ON <searchcondition>]
[WHERE <searchcondition>];
この新しい方法では、FROM 句で JOIN キーワードを使って結合を明示的に宣言します。
JOIN キーワードの左側の <tablerefleft> は比較に使用するテーブルで、このテーブルを左
テーブル(left table)と呼びます。また、<tablerefright> も比較の際に使用するテーブル
で、このテーブルを右テーブル(right table)と呼んでいます。結合の条件(各テーブルの
列)は、ON 句に記述します。WHERE 句には、返される行を限定するための検索条件を
記述します。たとえば、上記の従来の JOIN 処理を新しい記述方法で書き直すと、次のよう
になります。
EXEC SQL
DECLARE BIG_SAL CURSOR FOR
SELECT D.DEPARTMENT, D.MNGR_NO, E.SALARY
FROM DEPARTMENT D INNER JOIN EMPLOYEE E
ON D.MNGR_NO = E.EMP_NO
WHERE E.SALARY*2 > (SELECT SUM(S.SALARY) FROM EMPLOYEE S
WHERE D.DEPT_NO = S.DEPT_NO)
ORDER BY D.DEPARTMENT;
この新採用の JOIN 構文にはいくつか利点があります。まず、結合を明示的に記述すること
ができるため、ソースコードを読む際にプログラムの内容がはっきりします。
ON 句には結合条件を指定します。WHERE 句には、返される列を制限する条件を指定で
きます。
なお FROM 句では、使用するテーブルの優先順位を定義することもできます。したがっ
て、結合をネストして 3 つ以上のテーブルを結合することができます。結合のネストの詳
細は、6-52 ページの「ネストされた結合」を参照してください。
同等結合の記述
同等結合とは、2 つのテーブルのある列を使用して、同じ値をもとに行を取り出すような内
部結合を言います。この同等結合は、結合処理の中で最も頻繁に使用されます。同等結合で
は、ON 句は必ず、次のように等号を使って記述します。
ON t1.column = t2.column
たとえば、次の結合では、COUNTRIES テーブルの行のうち、CAPITAL 列の値(首都の
名前)と同じ値が CITIES テーブルの NAME 列にあれば、国名、首都名、首都の人口が
出力されます。
EXEC SQL
DECLARE CAPPOP CURSOR FOR
SELECT COU.NAME, COU.CAPITAL, CIT.POPULATION
FROM COUNTRIES COU JOIN CITIES CIT ON CIT.NAME = COU.CAPITAL
WHERE COU.CAPITAL NOT NULL
ORDER BY COU.NAME;
6-48 埋 め 込 み S Q L ガ イ ド
テーブルの結合
上の例では、ON 句で、CITIES テーブルの NAME 列の値が COUNTRIES テーブルの
CAPITAL 列の値と等しい場合に行が取り出されるように検索条件を記述しています。ま
た、WHERE 句では、NOT NULL を使用して、COUNTRIES テーブルの CAPITAL 列の
値が NULL のときは行を取り出さないように指定しています。
比較結合の記述
比較結合とは、比較演算子を使って 2 つのテーブルの列の値を比較し、行を取り出すよう
な内部結合を言います。また、非同等結合とも呼んでいます。たとえば比較結合では、一方
のテーブルの列の値ともう一方の列の値を比較し、どちらか値が小さい側の行を取り出す
といったことができます。比較結合の場合、ON 句は次のようになります。
ON t1.column <operator> t2.column
上で、<operator> は InterBase で使用可能な比較演算子を表します。有効な比較演算子の詳
細は、6-8 ページの「式での比較演算子の使い方」を参照してください。
たとえば、次の結合では、カナダの州のうち、アメリカのアラスカ州より面積の大きいも
のがあれば、その情報が出力されます。
EXEC SQL
DECLARE BIGPROVINCE CURSOR FOR
SELECT S.STATE_NAME, S.AREA, P.PROVINCE_NAME, P.AREA
FROM STATES S JOIN PROVINCE P ON P.AREA > S.AREA AND
P.COUNTRY = 'Canada'
WHERE S.STATE_NAME = 'Alaska';
この例では、まず、ON 句の比較演算子を使った検索条件によって、カナダの州全部とア
メリカの州全部とで相互に面積の比較が行われます。この後、WHERE 句により、アメリ
カの州がアラスカに限定され、最終的にアラスカよりも面積の大きいカナダの州が出力さ
れます。
自己結合の記述
自己結合とは、1 つのテーブルの中の列をもとにデータを取り出す内部結合を言います。た
とえば、RIVERS というテーブルに、河川名と、その河川が流れ込む合流河川が記録され
ていたとします。すべての河川が別の河川に合流しているわけではありません。このため、
このテーブルを使用して、別の河川に合流する河川を拾い出してみます。この場合は、自
己結合を利用できます。
EXEC SQL
DECLARE RIVERSTORIVERS CURSOR FOR
SELECT R1.RIVER, R2.RIVER
FROM RIVERS R1 JOIN RIVERS R2 ON R2.OUTFLOW = R1.RIVER
ORDER BY R1.RIVER, R2.SOURCE;
上の例のように、自己結合では、JOIN の両側のテーブルは同一のため、それぞれ異なる相
関名を付けなければなりません(上の例では、R1、R2 という相関名を割り当てている)。
相関名の割り当ておよび使い方の詳細は、6-26 ページの「相関名の宣言と使い方」を参照
してください。
第 6 章 データ処理
6-49
テーブルの結合
外部結合
外部結合では、一方のテーブルからすべての行が取り出され、もう一方のテーブルからは
検索条件に合致した行だけが取り出されます。このほか、両方のテーブルの行を全部取り出
すこともできますが、この外部結合はあまり利用されません。次の文は外部結合の構文です
が、内部結合の構文とよく似ています。
SELECT col [, col ...] | *
FROM <tablerefleft> {LEFT | RIGHT | FULL} [OUTER] JOIN
<tablerefright> [ON <searchcondition>]
[WHERE <searchcondition>];
外部結合には 3 つの型があり、いずれかを指定しなければなりません。指定できる型は次
のとおりです。
• 左外部結合(left outer join)では、JOIN キーワードの左側のテーブルの行が全部取り出
されます。右側のテーブルからは、検索条件に合致する行だけが取り出されます。
• 右外部結合(right outer join)では、JOIN キーワードにある右側のテーブルの行が全部
取り出されます。左側のテーブルからは、検索条件に合致する行だけが取り出されます。
• 完全外部結合(full outer join)では、ON 句の検索条件とは無関係に、左側と右側の両方
のテーブルの行が全部取り出されます。
外部結合は、検索条件に該当するデータを取り出すとともに、検索条件には合致しなくて
も関連するデータを同時に確認するときに有効です。たとえば、外部結合を使用して、河川
の源流がある国を取り出すと同時に、源流を持たない国を取り出して比較することができ
ます。
左外部結合の記述
外部結合の中では、左外部結合がよく利用されます。次は、左外部結合の例で、ここでは、
河川の源流を持つ国の他に、源流のない国も併せて取り出されます。なお、COUNTRIES
テーブルの COUNTRY 列の値が NULL のときは、国名は取り出されません。
EXEC SQL
DECLARE RIVSOURCE CURSOR FOR
SELECT C.COUNTRY, R.RIVER
FROM COUNTRIES C LEFT JOIN RIVERS R ON R.SOURCE = C.COUNTRY
ORDER BY C.COUNTRY;
ON 句を使用すると、FROM 句で結合の検索条件を指定できます。ON 句では、検索条件
を記述して、右テーブルの列の値をもとに行を限定することができます。一方、上の記述に
はありませんが、WHERE 句で検索条件を記述することによって、左テーブル(外部のテー
ブル)の列の値に基づいて行をさらに絞り込むことができます。
右外部結合の記述
右外部結合では、JOIN キーワードの右側にあるテーブルの行が全部取り出されます。これ
に対して、左側のテーブルからは、検索条件に合致する行だけが取り出されます。たとえ
ば、次の右外部結合では、河川名と、その源泉がある国名が取り出され、さらに河川の源
泉を持たない国も出力されます。
EXEC SQL
6-50 埋 め 込 み S Q L ガ イ ド
テーブルの結合
DECLARE RIVSOURCE CURSOR FOR
SELECT R.RIVER, C.COUNTRY
FROM RIVERS.R RIGHT JOIN COUNTRIES C ON C.COUNTRY = R.SOURCE
ORDER BY C.COUNTRY;
ヒント
上記の例でもそうですが、左外部結合の場合、LEFT を RIGHT に書き直して JOIN の両
側のテーブルを相互に入れ替えると、処理内容の同じ右外部結合ができます。逆の場合も
同様です。
完全外部結合の記述
完全外部結合では、ON 句の検索条件とは無関係に、左側と右側の両方のテーブルの行が
全部取り出されます。ただし、列に格納されている値が NULL のときは、データは取り出
されません。完全外部結合は、異なるテーブルに同種のデータが格納されている場合に、
データが間違っていないかどうかを検証するときなどに便利です。
たとえば、複数のテーブルに同じ都市名が散らばって収められていたとします。また、テー
ブルにトリガーが設定されておらず、このため別のテーブルに対するデータの自動入力は
行われていないものとします。この場合、各テーブルの都市名の照合と検査には、完全外
部 結 合 が 必 要 に な り ま す。
たとえば、次の例は、COUNTRIES、CITIES、
NATIONAL_PARKS という 3 つのテーブルから都市名を全部取り出す場合の完全外部結
合です。
EXEC SQL
DECLARE ALLCITIES CURSOR FOR
SELECT DISTINCT CIT.CITY, COU.CAPITAL, N.PARKCITY
FROM (CITIES CIT FULL JOIN COUNTRIES COU) FULL
JOIN NATIONAL_PARKS N;
上の例では、テーブルが 3 つあるため、最初の完全外部結合をネストにしてあります。こ
のため、最初に CITIES と COUNTRIES の 2 つのテーブルの行がすべて取り出され、そ
れをもとに結果テーブルが作成されます。続いて、この結果テーブルを左テーブルとして、
また NATIONAL_PARKS を右テーブルとして、完全外部結合処理が再度行われます。ネ
ストされた結合の詳細は、6-52 ページの「ネストされた結合」を参照してください。
メモ
複数のテーブルに同じデータまたは関連するデータを入力するときは、通常、トリガーを
作成すると便利です。このトリガーを使用して、異なるテーブルのデータを同時に更新す
ることができます。トリガーの詳細は、『データ定義ガイド』を参照してください。
外部結合のソート / マージの最適化
外部結合アルゴリズムのソート / マージ オプションは、外部結合の外部ストリームと内部
ストリームを認識するようになりました。また、一致する行が内部ストリームにない場合、
外部行を NULL 値を持つ内部行と一致させるようになりました。
完全外部結合(両側外部結合)の場合は、第 1 ストリームについて一致する行と NULL に
一致する行が生成された後、外部ストリームと内部ストリームが交換されます。第 1 スト
リームが内部ストリームになり、第 2 ストリームであったものが外部ストリームになりま
す。その次に、これらの行が左外部結合され、外部ストリームが NULL に一致する行のみ
が生成されます。結合条件に一致する行は除外されます。それらの行は、第 1 段階で 2 つ
のストリームが交換される前に生成されたからです。
第 6 章 データ処理
6-51
サブクエリー
ネストされた結合
SELECT 文の FROM 句では、かっこの中にテーブル(またはテーブルリファレンス)の
名前、JOIN キーワード、ON 句を記述し、ネストされた結合を作成することができます。
結合の実行時には、このネストされた結合の結果テーブルが作成されます。この結果テー
ブルは、データベースに格納されている実際のテーブルと同じように扱われます。このテー
ブルリファレンスは柔軟かつ強力で、これを活用することで 1 つの SELECT 文で複雑な結
合を要領よく記述することができます。
たとえば、次の文は、ネストされた左外部結合の例です。ここでは、まず、COUNTRIES
テーブルの COUNTRY 列の国名と同じ国名がない行も含め、CITIES テーブルに記録され
ている都市名が全部取り出され、結果テーブルが作成されます。次に、この結果テーブルを
左テーブルとして、内部結合が実行されます。この結果、プロのスポーツチームのある都
市だけが取り出され出力されます。また、そのチーム名とスポーツの種類も出力されます。
DECLARE SPORTSCITIES CURSOR FOR
SELECT COU.COUNTRY, C.CITY, T.TEAM, T.SPORT
FROM (CITIES CIT LEFT JOIN COUNTRIES COU ON COU.COUNTRY =
CIT.COUNTRY) INNER JOIN TEAMS T ON T.CITY = C.CITY
ORDER BY COU.COUNTRY;
左外部結合の詳細は、6-50 ページの「外部結合」を参照してください。
サブクエリー
サブクエリーとは、WHERE 句でかっこを使ってネストされた SELECT 文を言います。こ
のサブクエリーは、それ自体が検索条件として機能します。このため、サブクエリーを使
用して、外部クエリー(親クエリー)よりも下のレベルで行を限定することができます。サ
ブクエリーでは、親クエリーと同じテーブルを指定することもでき、別のテーブルを指定
することもできます。
サブクエリーの基本構文は次のようになります。
SELECT [DISTINCT] col [, col ...]
FROM <tableref> [, <tableref> ...]
WHERE {expression {[NOT] IN | comparison_operator}
| [NOT] EXISTS} (SELECT [DISTINCT] col [, col ...]
FROM <tableref> [, <tableref> ...]
WHERE <search_condition>);
サブクエリーは検索条件のため、通常、親クエリーより先に評価されます。この後、サブ
クエリーの結果をもとに、親クエリーによって取り出す行が決定されます。ただし、サブク
エリーが相関的なサブクエリーの場合は例外です。相関的なサブクエリーでは、親クエリー
から情報を得て、その後で処理が行われます。相関的なサブクエリーの詳細は、6-54 ペー
ジの「相関的なサブクエリー」を参照してください。
サブクエリーは、親クエリーの WHERE 句にどういった演算子が指定されているかによっ
て、記述の方法が異なります。記述の方法には、次の 3 とおりあります。
6-52 埋 め 込 み S Q L ガ イ ド
サブクエリー
• 親クエリーの WHERE 句に IN 演算子が使用されている場合は、複数の値が返されるよう
にサブクエリーを記述します。また、比較演算子とともに ALL、ANY、SOME のいずれ
かの演算子が使用されているときも同様です。
• 親クエリーの WHERE 句に比較演算子が使用されているときは、サブクエリーは 1 つの値
が返されるように記述しなければなりません。
• 親クエリーの WHERE 句に EXISTS 演算子が使用されているときは、EXISTS が必要と
する構文を使って記述しなければなりません。
サブクエリーの中には、かっこを使ってさらにサブクエリーをネストすることができます。
このサブクエリーも検索条件となり、このようにして、何層にもわたってサブクエリーを
作成することができます。
基本的なサブクエリー
あるテーブルの中の列をもとにデータを取り出す場合、自己結合を使用できます。ただし、
自己結合ではデータの取り出しが難しいこともあり、サブクエリーは、このような場合に
利用価値があります。たとえば、COUNTRIES テーブルに国名と面積が記録されており、
このテーブルを使って面積が平均より大きい国を検索する場合、この処理は自己結合では
できません。一方、サブクエリーを使用すると検索が容易です。
EXEC SQL
DECLARE LARGECOUNTRIES CURSOR FOR
SELECT COUNTRY, AREA
FROM COUNTRIES
WHERE AREA > (SELECT AVG(AREA) FROM COUNTRIES);
ORDER BY AREA;
上の例では、親クエリーでもサブクエリーでも同じテーブルを指定しています。これに対し
て、指定先のテーブルは親クエリーとサブクエリーで異なっていてもかまいません。たとえ
ば、次は、親クエリーで CITIES テーブルを、サブクエリーで COUNTRIES テーブルを指
定している例です。
EXEC SQL
DECLARE EUROCAPPOP CURSOR FOR
SELECT CIT.CITY, CIT.POPULATION
FROM CITIES CIT
WHERE CIT.CITY IN (SELECT COU.CAPITAL FROM COUNTRIES COU
WHERE COU.CONTINENT = 'Europe')
ORDER BY CIT.CITY;
また、上の例では、相関名を使ってテーブルを区別していますが、親クエリーとサブクエ
リーで指定するテーブルが異なる場合は、相関名は必要ありません。一方、親クエリーとサ
ブクエリーで指定するテーブルが同じで、また同じ列名を使って処理を行う場合には相関
名が必要です。ただし、どのような場合でも相関名を使用することが優れたプログラミン
グ技法と言えます。相関名の使い方の詳細は、6-26 ページの「相関名の宣言と使い方」を
参照してください。
第 6 章 データ処理
6-53
サブクエリー
相関的なサブクエリー
サブクエリーのうち、親クエリーによって与えられる値をもとに処理を行うサブクエリー
を相関的なサブクエリーと呼んでいます。また、親クエリーによって評価される行はそれぞ
れ値が異なるため、相関的なサブクエリーでは、その各行について一度ずつ処理が行われ
るのが一般的です。
たとえば、次は、CITIES テーブルの中に同じ国の都市名が 3 つ以上あったときに、その
国名が出力されるクエリーの例です。ここでは、親クエリーによって、COUNTRIES テー
ブルに格納されている国名が行ごとに 1 つずつ取り出されます。取り出された国名は、次
に、サブクエリーの WHERE 句に渡され、その国に属する都市が CITIES テーブルの CITY
列にあるかどうかが検査されます。その国に属する都市が見つかったときは、COUNT() 関
数によってカウントされます。このようにして、COUNT() 関数の値が 3 になったとき、親
クエリーによって渡された行が最終的に取り出されます。
EXEC SQL
DECLARE TRICITIES CURSOR FOR
SELECT COUNTRY
FROM COUNTRIES COU
WHERE 3 <= (SELECT COUNT (*)
FROM CITIES CIT
WHERE CIT.CITY = COU.CAPITAL);
上の例は簡単な相関的なサブクエリーですが、このようなサブクエリーをいくつかネスト
して組み合わせることで、複雑なクエリーを作成できます。たとえば、次は、その国の面積
がすべての国の平均面積より大きく、かつ海抜 30 メートル以下に少なくとも 1 つは都市の
ある国が見つかった場合、その国名、首都、最大都市が出力されるクエリーの例です。
EXEC SQL
DECLARE SEACOUNTRIES CURSOR FOR
SELECT CO1.COUNTRY, C01.CAPITAL, CI1.CITY
FROM COUNTRIES C01, CITIES CI1
WHERE CO1.COUNTRY = CI1.COUNTRY AND CI1.POPULATION =
(SELECT MAX(CI2.POPULATION)
FROM CITIES CI2 WHERE CI2.COUNTRY = CI1.COUNTRY)
AND CO1.AREA >
(SELECT AVG (CO2.AREA)
FROM COUNTRIES C02 WHERE EXISTS
(SELECT *
FROM CITIES CI3 WHERE CI3.COUNTRY = CO2.COUNTRY
AND CI3.ALTITUDE <= 30));
上の例のように、サブクエリーを使って複数のクエリーを組み合わせる場合、クエリーで
使用するテーブル(FROM 句で指定するテーブル)に対して、そのクエリー専用の相関名
を宣言しなければなりません。また、クエリーの WHERE 句では、この専用の相関名を
使って列を指定し、検索条件を記述する必要があります。この方法で、所定の内容のテーブ
ル(WHERE 句で行が限定されたテーブル)を使って SELECT 句の処理が行われるよう
になります。相関名の詳細は、6-26 ページの「相関名の宣言と使い方」を参照してください。
6-54 埋 め 込 み S Q L ガ イ ド
データの挿入
UPDATE 文における相関サブクエリーのインデックス最適化
適切なインデックスが定義されている場合、UPDATE 文内の相関サブクエリーでは、イン
デックスによる検索を用いて行が取り出されるようになりました。UPDATE 文内の相関サブ
クエリーでインデックスによるアクセスを行うには、以下のサンプル コードのように記述
します。
UPDATE A SET A.C1 = (SELECT B.C1 FROM B WHERE B.C2 = A.C2)
B.C2 にインデックスが設定されている場合、外部テーブル A の行は既に取得されているの
で、InterBase はインデックスを用いて、B.C2 = A.C2 の条件に一致する行をテーブル B
から取り出します。
データの挿入
テーブルに新規の行を挿入(追加)する場合、INSERT 文を使用します。なお、新規の行
の追加が可能なのは、そのテーブルについて INSERT 特権が与えられているユーザーに限
られます。また、ストアドプロシージャによる追加の場合も、そのストアドプロシージャ
にテーブルの INSERT 特権が与えられていることが必要です。
INSERT 文では、データの挿入方法に次の 2 種類があります。
• VALUES 句による挿入:複数の値を指定して追加するときに使用します。指定する値と
しては、ハードコードされた値のほか、変数も記述できます。
• SELECT 文による挿入:既存のテーブルの値を取り出し、その値を別のテーブルに追加す
る場合に使用します。
INSERT の構文は次のとおりです。
INSERT [TRANSACTION name] INTO table [(col [, col ...])]
{VALUES (<val>[:ind] [, <val>[:ind] ...])
| SELECT <clause>};
上の構文で、INTO の右にはテーブル名を、その右にはテーブルの列名を指定します。こ
こで指定した列にデータが格納されます。この列名は DSQL アプリケーションでは記述し
なくてもかまいません。DSQL アプリケーションで列名を記述しなかった場合、各データ
が、そのテーブルに設定されている列の順番で挿入されます。また、挿入するデータの数が
列の数より少なかったときは、余った列には 0 が格納されます。
VALUES によるデータの挿入
VALUES 句では複数の値を指定することができ、ここで指定した値を持つ行がテーブルに
挿入されます。また、実行時にユーザーが値を追加する場合も、この VALUES 句を使用
します。VALUES 句では、ハードコードされた値の他に変数を指定することもできます。
たとえば、次の文では、DEPARTMENT テーブルに新規の行が追加されると、数値と文字
列の 2 つのハードコードされた値がそれぞれ DEPT_NO と DEPARTMENT という 2 つ
の列に挿入されます。
EXEC SQL
INSERT INTO DEPARTMENT (DEPT_NO, DEPARTMENT)
第 6 章 データ処理
6-55
データの挿入
VALUES (7734, 'Marketing');
上の例で、VALUES 句で指定している列の数は 2 つですが、DEPARTMENT テーブルの
列は他にもあります。この場合、値が挿入されなかった列には NULL が格納されます。
次の C のコードは、ユーザーにデータの入力を求め、そのデータを VALUES 句を使って
DEPARTMENT テーブルに追加する例です。ここでは、VALUES 句で変数を指定し、こ
の変数を介して値を挿入しています。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
char department[26], dept_no[16];
int dept_num;
EXEC SQL
END DECLARE SECTION;
. . .
printf("Enter name of department: ");
gets(department);
printf("¥nEnter department number: ");
dept_num = atoi(gets(dept_no));
EXEC SQL
INSERT INTO COUNTRIES (DEPT_NO, DEPARTMENT)
VALUES (:dept_num, :department);
VALUES 句で変数を指定する場合、各変数の先頭には必ずコロン(:)を付けます。この
コロンがないと、変数がテーブルの列名として解釈されてしまいます。
SELECT によるデータの挿入
SELECT 文を使用して、テーブルにすでに格納されている行を取り出し、同じテーブルまた
は別のテーブルに追加することができます。SELECT 文では、VALUES 句と同じく挿入す
る値を直接指定できるほか、列名を使って挿入する値を指定することもできます。たとえば、
次の INSERT 文は、OLDDEPT テーブルの行のうち、DEPARTMENT 列の値が
"Documentation" である行を取り出し、その行を DEPARTMENT テーブルに追加(コピー)
する例です。データとしては、部署番号、部署名、
予算が挿入されます。また、SELECT 文
で直接 "Publications" を指定しているため、この値が DEPARTMENT テーブルの
DEPARTMENT 列に挿入されることになります。
EXEC SQL
INSERT INTO DEPARTMENTS (DEPT_NO, DEPARTMENT, BUDGET)
SELECT DEPT_NO, 'Publications', BUDGET
FROM OLDDEPT
WHERE DEPARTMENT = 'Documentation';
SELECT では、算術演算子を記述することもできます。たとえば、社員管理のアプリケー
ションで、名前の他に社員番号を連番で割り当てているとします。したがって、新しく社員
が入社したときは、EMPLOYEE テーブルに新しい行を追加するとともに、番号を重複し
ないように割り当てる必要があります。このような場合、SELECT 文を使用して、それま
での番号のうち最大のものを探し、それに 1 を加えるのが便利です。次の文は、その処理
6-56 埋 め 込 み S Q L ガ イ ド
データの挿入
例です。例では、SELECT で lastname と firstname という 2 つの変数を指定し、この変数
を介してデータをそれぞれ LAST_NAME と FIRST_NAME の 2 つの列に挿入していま
す。
EXEC SQL
INSERT INTO EMPLOYEE (EMP_NO, LAST_NAME, FIRST_NAME)
SELECT (MAX(EMP_NO) + 1, :lastname, :firstname)
FROM EMPLOYEE;
NULL を含んだ新規行の追加
テーブルに新規の行を追加する場合、列によっては値が必要ないか、その時点で挿入する
値が未定なこともあります。このような場合は、行の挿入時に、その列に NULL を割り当
てておくようにします。列に NULL を挿入する方法には、次の 3 種類があります。
• 列を無視します(故意に列を指定しない)。
• NULL 値を指定して挿入します。SQL では、これが一般的な方法です。
• インジケータ変数を使用します。(ユーザーが値を入力しなかったときには NULL が格納
されますが、インジケータ変数を使用することで、この NULL をトラップできます。
列の無視による NULL の挿入
INTO 句で列を指定しない場合、その列には自動的に NULL が割り当てられます。これは、
挿入時、参照元の値が見つからない列に対して、フラグとして NULL が自動的に格納され
るためです(列の属性が NULL に設定されている場合)。たとえば、DEPARTMENT テー
ブルに HEAD_DEPT、MNGR_NO、BUDGET という列があったとします。この場合、こ
の 3 つの列を指定から除外して、次のような INSERT 文を記述したとします。
EXEC SQL
INSERT INTO DEPARTMENT (DEPT_NO, DEPARTMENT)
VALUES (:newdept_no, :newdept_name);
上の例では、HEAD_DEPT、MNGR_NO、BUDGET という列を指定していないため、追
加された行では、この 3 つの列には NULL 値が格納されます。
メモ
既存のテーブルに列を追加した場合、すべての行のその列に NULL が格納されます。
指定による NULL の挿入
行の挿入時、列の値を特に指定しない場合、その列には NULL が挿入されます。SQL で
は、列に NULL を挿入する際、この方法がよく使用されます。このほか、InterBase では、
INSERT 文で NULL を指定して、列に NULL を格納することもできます。
たとえば、次の INSERT 文では、DEPARTMENT テーブルに新規の行が追加されると同
時に、7 つの列に値が格納されます。このうち、4 つの列には NULL が挿入されます。
EXEC SQL
INSERT INTO DEPARTMENT
(DEPT_NO, DEPARTMENT, HEAD_DEPT, MNGR_NO, BUDGET,
LOCATION, PHONE_NO)
VALUES (:dept_no, :dept_name, NULL, NULL, 1500000, NULL, NULL);
第 6 章 データ処理
6-57
データの挿入
インジケータ変数による NULL の挿入
NULL をトラップし、割り当てる方法として、インジケータ変数を使用する方法がありま
す。この方法は、ユーザーによるデータの入力があり、また値の未入力も受け付ける(ユー
ザーがデータを入力しない場合も対応できる)アプリケーションでは、必ず使用しなけれ
ばなりません。また、インジケータ変数を使用することで、NULL の検索が可能になりま
す。つまり、InterBase ではユーザーからの値の入力がなかった場合、列には自動的に
NULL が格納されます。このとき、列のデータ型が数値だった場合、NULL は複数の 0 と
して格納されます。また、列のデータ型が文字だったときは、NULL は複数のスペースと
して格納されます。ただし、この 0 とスペースはいずれも有効なデータとして認識される
ため、ユーザーが入力したデータが NULL であるか、または実際の 0 またはスペースなの
か区別がつかなくなることもあります。
このような場合、インジケータ変数を使用することにより、ユーザーの入力が NULL で
あったかどうかを識別することができます。このインジケータ変数には、データが NULL
であるかどうかのフラグを格納します。インジケータ変数の記述は、次の手順で行います。
1
2
インジケータ変数として使用する変数を宣言します。
ユーザーが入力した値をチェックし、インジケータ変数を次のいずれかの値に設定します。
0
–1
ユーザーの入力を受ける変数にデータがあった場合
ユーザーの入力を受ける変数にデータがなかった場合
3 INSERT 文で、変数とインジケータ変数を関連付けます。この場合の構文は次のとおりで
す。
INSERT INTO table (<col> [, <col> ...])
VALUES (:variable [INDICATOR] :indicator
[, :variable [INDICATOR] :indicator ...]);
メモ
INDICATOR キーワードはオプションです。
たとえば、次は、ユーザーに部署名、部署番号、部署の予算の入力を求める C のコード例
です。このうち予算の入力に対して検査を行います。値の入力がなかった場合、インジケー
タ変数である bi を –1 に設定しています。一方、入力があったときには bi を 0 に設定して
います。プログラムの最後では、INSERT を使って DEPARTMENT テーブルにデータを
格納しています。したがって、ユーザーの入力がないため BUDGET 列に NULL が格納さ
れたときでも、それが NULL なのか、実際の 0 またはスペースなのかをこのインジケータ
変数で確認することができます。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
short bi; /* インジケータ変数の宣言 */
char department[26], dept_no_ascii[26], budget_ascii[26];
long num_val; /* 予算を挿入するためのホスト変数 */
short dept_no;
EXEC SQL
END DECLARE SECTION;
. . .
6-58 埋 め 込 み S Q L ガ イ ド
データの挿入
printf("Enter new department name: ");
gets(cidepartment);
printf("¥nEnter department number: ");
gets(dept_no_ascii);
printf("¥nEnter department’s budget: ");
gets(budget_ascii);
if (budget_ascii = "")
{
bi = -1; num_val = 0;
}
else
{
bi = 0;
num_val = atoi(budget_ascii);
}
dept_no = atoi(dept_no_ascii);
EXEC SQL
INSERT INTO DEPARTMENT (DEPARTMENT, DEPT_NO, BUDGET)
VALUES (:department, :dept_no, :num_val INDICATOR :bi);
. . .
上の例は、NULL を格納する際のインジケータ変数の使い方ですが、テーブルからデータ
を取り出す場合もインジケータ変数を使って NULL を検査することができます。テーブル
から取り出した NULL のトラップの詳細は、6-40 ページの「INDICATOR によるインジ
ケータ変数の設定」を参照してください。
ビューを使ったデータの挿入
新規の行はビューを介してテーブルに挿入することもできます。ただし、この場合、次の
ような条件が必要になります。
• ビューが更新可能であること。更新可能なビューの詳細は、『データ定義ガイド』を参照
してください。
• 使用するビューに WITH CHECK OPTION が設定されていること
• 使用するビューの INSERT 特権がユーザーに与えられていること。ストアドプロシージャ
の場合は、そのストアドプロシージャに対して、使用するビューの INSERT 特権が与えら
れていること
ビューを使って行を追加する場合、データの挿入が可能なのは、そのビューに用意されて
いる列に限られます。したがって、行の追加先となるテーブルの列のうち、ビューに用意さ
れていない列に対しては、自動的に NULL が格納されます。たとえば、PART_DEPT とい
うビューがあり、このビューは次のようにして作成されていたとします。
EXEC SQL
CREATE VIEW PART_DEPT
(DEPARTMENT, DEPT_NO, BUDGET)
AS SELECT DEPARTMENT, DEPT_NO, BUDGET
第 6 章 データ処理
6-59
データの挿入
FROM DEPARTMENT
WHERE DEPT_NO NOT NULL AND BUDGET > 50000
WITH CHECK OPTION;
上の例では、PART_DEPT ビューの作成元となっているテーブルは DEPARTMENT だけ
です。また、列としては DEPARTMENT、DEPT_NO、BUDGET の 3 つが指定されてお
り、この 3 つの列に対してデータの挿入が可能です。さらに、WITH CHECK OPTION が
設定されているので、挿入できるのは、ビューで指定されている範囲の値(この例では、
BUDGET が 50000 を超える)だけです。この PART_DEPT ビューを使用して、新規の行
を DEPARTMENT テーブルに追加する場合は、次のようになります。ここでは、部署が
Publications のデータを 1 行追加します。
EXEC SQL
INSERT INTO PART_DEPT (DEPARTMENT, DEPT_NO, BUDGET)
VALUES ('Publications', '7735', 1500000);
上でも説明したように、テーブルにあってビューにない列に対しては NULL が格納されま
す。したがって、ビューに用意されていない列が DEPARTMENT テーブルにあったとき
は、その列にすべて NULL が入力されます。
ビューの作成の詳細は、第 5 章「データ定義文」を参照してください。CREATE VIEW 構
文の詳細は、『言語リファレンス』を参照してください。
メモ
トリガーを使用して、更新不可のビューを更新する方法のヒントについては、
『データ定義
ガイド』の「トリガーの操作」を参照してください。
INSERT でのトランザクション名の指定
InterBase では、SQL アプリケーションが複数のトランザクションを同時に実行できます。
ただし、次の条件が必要です。
• トランザクションがそれぞれ SET TRANSACTION 文で事前に宣言されていること。ト
ランザクション処理と命名の詳細は、第 4 章「トランザクションの操作」を参照してくだ
さい。
• データ操作文(SELECT、INSERT、UPDATE、DELETE、DECLARE、OPEN、FETCH、
CLOSE)で TRANSACTION 句が使用されており、関連するトランザクションが指定さ
れていること。
• SQL 文が DSQL ではないこと。DSQL では、ユーザーがトランザクション名を定義するこ
とはできません。
上 記 の よ う に、TRANSACTION 句でトランザクション名を指定することによって、
INSERT 文でも任意のトランザクションを使ってテーブルに対する行の挿入が可能になり
ます。この場合、構文は次のようになります。
INSERT TRANSACTION name INTO table (col [, col ...])
なお、トランザクションを 1 つしか使用しないプログラムでは、TRANSACTION 句は記
述しなくてもかまいません。TRANSACTION 句の記述は、複数のトランザクションを使
用するプログラムだけで必要です。ただし、複数のトランザクションを使用するプログラ
ムでも、デフォルトトランザクション(GDS__TRANS)を使用しない場合は記述しなけ
ればなりません。たとえば、次のように記述した場合、INSERT は T1 というトランザク
ションとともに機能し、T1 のもとで挿入が行われるようになります。
6-60 埋 め 込 み S Q L ガ イ ド
データの更新
EXEC SQL
INSERT TRANSACTION T1 INTO DEPARTMENT (DEPARTMENT, DEPT_NO, BUDGET)
VALUES (:deptname, :deptno, :budget INDICATOR :bi);
データの更新
既存の行のデータを更新(変更)するときは、UPDATE 文を使用します。ただし、行の更
新が可能なのは、そのテーブルについて UPDATE 特権が与えられているユーザーに限られ
ます。また、ストアドプロシージャによる更新の場合も、そのストアドプロシージャにテー
ブルの UPDATE 特権が与えられていなければなりません。UPDATE の構文は次のとおり
です。
UPDATE [TRANSACTION name] table
SET col = <assignment> [, col = <assignment> ...]
WHERE <search_condition> | WHERE CURRENT OF cursorname
[ORDER BY <order_list>]
[ROWS <value> [TO <upper_value>] [BY <step_value>][PERCENT][WITH
TIES]];
UPDATE では、SET 句を使って指定した列が更新の対象となります。言い換えれば、SET
句で指定しなかった列は更新されません。<assignment> には値を指定します。既存の値は、
この値に置き換えられます。また、1 つの UPDATE 文で、テーブルの複数行に対して一度
に変更を行うこともできます。次の例は、1 行のデータを変更する場合の記述です。
EXEC SQL
UPDATE DEPARTMENT
SET DEPARTMENT = 'Publications'
WHERE DEPARTMENT = 'Documentation';
上の例では、WHERE 句の検索条件により、変更対象の行が 1 行に限定されています。同
じ変更をテーブルの複数の行に反映する場合が、WHERE 句としては一般的です。たとえ
ば、次の例では、EMPLOYEE テーブル(社員名簿)の DEPARTMENT 列(所属部署)に
“Documentation” という値が入っている場合、その値がすべて “Publications” に変更され
ます。UPDATE 文は、次のようになります。
EXEC SQL
UPDATE DEPARTMENT
SET DEPARTMENT = 'Publications'
WHERE DEPARTMENT = 'Documentation';
上記のように、複数行にわたって同じ変更を行う処理を大量更新または検索更新と呼んで
います。
なお、UPDATE 文の WHERE 句では、サブクエリーを記述することもできます。このサ
ブクエリーでは、複数のテーブルを指定することもできます。サブクエリーの詳細は、6-52
ページの「サブクエリー」を参照してください。
複数行の更新
複数行を更新する方法には、基本的に次の 2 種類があります。
第 6 章 データ処理
6-61
データの更新
• 検索更新:この方法では、複数行のデータに対して同一の変更が一度に実行されます。カー
ソルを使用せずに複数行のデータを自動更新するときに有効です。
• 位置指定更新:カーソルを使って行を取り出し、1 行ごとに更新を行う方法です。ユーザー
による更新を扱う場合に有効な方法で、ユーザーの指示にしたがって行を取り出し、その
つど更新されるように記述します。
位置指定更新よりも検索更新の方がプログラミングは簡単ですが、位置指定更新の方が処
理の点で柔軟です。
検索更新の記述
検索更新は、複数行に対して同じ変更を行う場合に使用します。この更新では、UPDATE
の SET 句に列と値を指定しておいた場合、WHERE 句の検索条件で取り出された行すべて
の該当列(SET 句で指定した列)に対して、変更が実行されます。SET 句で指定する値は、
定数または変数のどちらでもかまいません。
たとえば、次に挙げる検索更新の C のコード例では、ユーザーに国名と人口の変動率(パー
セント)の入力を求め、入力された値にしたがって検索更新を行っています。ここでは、変
動率が入力されると、その率にしたがって、ユーザーが指定した国に属する全都市の人口
(POPULATION 列のデータ)が一括して増減されます。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
char country[26], asciimult[10];
int multiplier;
EXEC SQL
END DECLARE SECTION;
. . .
main ()
{
printf("Enter country with city populations needing adjustment: ");
gets(country);
printf("¥nPercent change (100%% to -100%%:");
gets(asciimult);
multiplier = atoi(asciimult);
EXEC SQL
UPDATE CITIES
SET POPULATION = POPULATION * (1 + :multiplier / 100)
WHERE COUNTRY = :country;
if (SQLCODE && (SQLCODE != 100))
{
isc_print_sqlerr(SQLCODE, isc_status);
EXEC SQL
ROLLBACK RELEASE;
}
else
6-62 埋 め 込 み S Q L ガ イ ド
データの更新
{
EXEC SQL
COMMIT RELEASE;
}
}
重要
データが配列の場合は、データ型にかかわらず検索更新は実行できません。
位置指定更新の記述
カーソルを使って行を選択して更新を行う方法を位置指定更新と言います。ユーザーに入
力を求め、その入力にしたがって行を 1 行ずつ取り出して更新を行う場合に使用します。
この更新では、ユーザーは変更前の値と変更後の値を確認できます。位置指定更新は、次の
7 つの手順で行われます。
1
2
更新処理で必要になる変数を宣言します。
カーソルを宣言します。このカーソルは、更新の対象となる行の取り出しに使用しま
す。また、DSQL の場合は、FOR UPDATE 句を記述します。カーソルの宣言と使い方
の詳細は、6-36 ページの「複数行の取り出し」を参照してください。
3 カーソルを開きます。
4 行を取り出します。
5 現在値が表示され、新しい値の入力を求められます。
6 WHERE CURRENT OF 句を使用して、現在取り出されている行を更新します。
7 上のステップ 3 ~ 6 の処理を繰り返し、取り出し対象となっている行をすべて更新し
ます。
次の文は、位置指定更新の C のコード例です。ここでは、CITIES テーブルに格納されて
いる都市のうち、ユーザーが入力した国に属する都市が順次取り出され、さらにユーザー
が入力した率(パーセント)にしたがって、その都市の人口(POPULATION 列のデータ)
が増減されます。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
char country[26], asciimult[10];
int multiplier;
EXEC SQL
END DECLARE SECTION;
. . .
main ()
{
EXEC SQL
DECLARE CHANGEPOP CURSOR FOR
SELECT CITY, POPULATION
FROM CITIES
WHERE COUNTRY = :country;
第 6 章 データ処理
6-63
データの更新
printf("Enter country with city populations needing adjustment: ");
gets(country);
EXEC SQL
OPEN CHANGEPOP;
EXEC SQL
FETCH CHANGEPOP INTO :country;
while(!SQLCODE)
{
printf("¥nPercent change (100%% to -100%%:");
gets(asciimult);
multiplier = atoi(asciimult);
EXEC SQL
UPDATE CITIES
SET POPULATION = POPULATION * (1 + :multiplier / 100)
WHERE CURRENT OF CHANGEPOP;
EXEC SQL
FETCH CHANGEPOP INTO :country;
if (SQLCODE && (SQLCODE != 100))
{
isc_print_sqlerr(SQLCODE, isc_status);
EXEC SQL
ROLLBACK RELEASE;
exit(1);
}
}
EXEC SQL
COMMIT RELEASE;
}
重要
上の例では FOR UPDATE を記述していませんが、記述した場合、カーソルによって一度
に 1 行ずつ取り出されます。FOR UPDATE を省略した場合、行は一括して取り出されま
す。
UPDATE による列への NULL の設定
UPDATE では、列の値を NULL に設定することもできます。方法は、UPDATE の SET
句を使用し、NULL を格納する列を指定します。たとえば、マネージャのいない部署の予
算の値を NULL に設定する場合、記述は次のようになります。
EXEC SQL
UPDATE DEPARTMENT
SET BUDGET = NULL
WHERE MNGR_NO = NULL;
6-64 埋 め 込 み S Q L ガ イ ド
データの更新
ビューを使った更新
既存の行はビューを介して更新することもできます。ただし、次のような条件が必要にな
ります。
• ビューが更新可能であること。更新可能なビューの詳細は、『データ定義ガイド』を参照
してください。
• 使用するビューに WITH CHECK OPTION が設定されていること
• 使用するビューの UPDATE 特権がユーザーに与えられていること。ストアドプロシー
ジャの場合は、そのストアドプロシージャに対して、使用するビューの UPDATE 特権が
与えられていること
ビューを使って更新を行う場合、更新が可能なのは、そのビューに用意されている列に限
られます。たとえば、PART_DEPT というビューがあり、このビューは次のようにして作
成されていたとします。
EXEC SQL
CREATE VIEW PART_DEPT
(DEPARTMENT, NUMBER, BUDGET)
AS SELECT DEPARTMENT, DEPT_NO, BUDGET
FROM DEPARTMENT
WITH CHECK OPTION;
上の例では、PART_DEPT ビューの作成元となっているテーブルは DEPARTMENT だけ
で、列としては DEPARTMENT、DEPT_NO、BUDGET の 3 つが指定されています。し
たがって、ビューを介して更新を行う場合、この 3 つの列に限って処理が可能です。さら
に、WITH CHECK OPTION が設定されているので、ビューで指定されている範囲の値だ
けが挿入可能です(上の例では、具体的な範囲の記述は省略)。たとえば、PART_DEPT
ビューを使用して、DEPARTMENT テーブルの "Publications" 部の予算を更新する場合、
記述は次のようになります。
EXEC SQL
UPDATE PART_DEPT
SET BUDGET = 2505700
WHERE DEPARTMENT = 'Publications';
ビューの作成の詳細は、第 5 章「データ定義文」を参照してください。CREATE VIEW 構
文の詳細は、『言語リファレンス』を参照してください。
メモ
トリガーを使用して、更新不可のビューを更新する方法のヒントについては、
『データ定義
ガイド』の「トリガーの操作」を参照してください。
UPDATE でのトランザクション名の指定
InterBase では、SQL アプリケーションが複数のトランザクションを同時に実行できます。
ただし、次の条件が必要です。
• トランザクションがそれぞれ SET TRANSACTION 文で事前に宣言されていること。ト
ランザクション処理と命名の詳細は、第 4 章「トランザクションの操作」を参照してくだ
さい。
第 6 章 データ処理
6-65
データの削除
• データ操作文(SELECT、INSERT、UPDATE、DELETE、DECLARE、OPEN、FETCH、
CLOSE)で TRANSACTION 句が使用されており、関連するトランザクションが指定さ
れていること。
• SQL 文が DSQL ではないこと。DSQL では、ユーザーがトランザクション名を定義するこ
とはできません。
上 記 の よ う に、TRANSACTION 句でトランザクション名を指定することによって、
UPDATE 文で任意のトランザクションを使った更新が可能になります。この場合、構文は
次のようになります。
UPDATE [TRANSACTION name] table
SET col = <assignment> [, col = <assignment> ...]
WHERE <search_condition> | WHERE CURRENT OF cursorname;
複数のトランザクションを使用するプログラムでは、必ず TRANSACTION 句を記述しな
ければなりません。一方、トランザクションを 1 つしか使用しないプログラムでは、
TRANSACTION 句は記述しなくてもかまいません。また、複数のトランザクションを使
用するプログラムでも、同時に複数のトランザクションを使用することがない場合は、記
述の必要はありません。たとえば、次のように記述した場合、UPDATE は T1 というトラ
ンザクションとともに機能し、T1 のもとで更新が行われるようになります。
EXEC SQL
UPDATE TRANSACTION T1 DEPARTMENT
SET BUDGET = 2505700
WHERE DEPARTMENT = 'Publications';
データの削除
既存の行を削除するときには DELETE 文を使用します。行の削除が可能なのは、そのテー
ブルについて DELETE 特権が与えられているユーザーに限られます。また、ストアドプロ
シージャによる削除の場合も、そのストアドプロシージャにテーブルの DELETE 特権が付
与されていることが必要です。
DELETE の構文は次のとおりです。
DELETE [TRANSACTION name] FROM table
WHERE <search_condition> | WHERE CURRENT OF cursorname
[ORDER BY <order_list>]
[ROWS <value> [TO <upper_value>] [BY <step_value>][PERCENT][WITH
TIES]];
DELETE で削除を行った場合、FROM 句で指定した行は、列のデータ型にかかわらずテー
ブルから完全に削除されます。
DELETE では、テーブル中の 1 行のほか、複数行を一度に削除することもできます。次の
文は、
DEPARTMENT テーブルから “Channel Marketing” を含んでいる 1 行を削除する例
です。
EXEC SQL
DELETE FROM DEPARTMENT
WHERE DEPARTMENT = 'Channel Marketing';
6-66 埋 め 込 み S Q L ガ イ ド
データの削除
上の例では、WHERE 句の検索条件により、削除対象の行が 1 行に限定されています。同
じ削除条件をテーブルの複数の行に適用する場合が、WHERE 句としては一般的です。た
とえば、DEPARTMENT テーブルの行のうち、BUDGET 列の値(予算)が 1000000 ドル
未満の行をすべて削除する場合は、DELETE 文は次のようになります。
EXEC SQL
DELETE FROM DEPARTMENT
WHERE BUDGET < 1000000;
上の文のように、複数行を一度に削除する処理を大量削除または検索削除と呼んでいます。
なお、DELETE 文の WHERE 句では、サブクエリーを記述することもできます。このサ
ブクエリーでは、複数のテーブルを指定することもできます。サブクエリーの詳細は、6-52
ページの「サブクエリー」を参照してください。
複数行の削除
複数行を削除する方法には、基本的に次の 2 種類があります。
• 検索削除:この方法では、検索条件に該当する行がすべて削除されます。複数行を自動削
除するときに有効です。
• 位置指定削除:カーソルを使って行を取り出し、1 行ごとに削除を行う方法です。ユーザー
による削除を扱う場合に有効な方法で、行を取り出し、ユーザーの許可があれば削除され
るように記述します。
位置指定削除よりも検索削除の方がプログラミングは簡単ですが、位置指定削除の方が処
理の点で柔軟です。
検索削除の記述
検索削除では、WHERE 句で記述されている検索条件に合致する行を一括して削除するこ
とができます。たとえば、次は、ユーザーに国名の入力を求め、入力された国に属する都市
を一括して削除する場合の C のコード例です。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
char country[26];
EXEC SQL
END DECLARE SECTION;
. . .
main ()
{
printf("Enter country with cities to delete: ");
gets(country);
EXEC SQL
DELETE FROM CITIES
WHERE COUNTRY = :country;
if(SQLCODE && (SQLCODE != 100))
第 6 章 データ処理
6-67
データの削除
{
isc_print_sqlerr(SQLCODE, isc_status);
EXEC SQL
ROLLBACK RELEASE;
}
else
{
EXEC SQL
COMMIT RELEASE;
}
}
位置指定削除の記述
カーソルを使って行を選択して削除を行う方法を位置指定削除と言います。この削除では、
1 行ごとにユーザーによる削除の許可を求めます。また、削除前の値と削除後の値を表示し
ます。位置指定削除は、次の 7 つの手順で行われます。
1
2
削除処理で必要になる変数を宣言します。
カーソルを宣言します。このカーソルは、削除の対象となる行の取り出しに使用しま
す。また、FOR UPDATE 句を記述します。カーソルの宣言と使い方の詳細は、6-36
ページの「複数行の取り出し」を参照してください。
3 カーソルを開きます。
4 行を取り出します。
5 現在値が表示され、削除の許可が求められます。
6 WHERE CURRENT OF 句を使用して、現在取り出されている行を削除します。
WHERE CURRENT OF 句ではカーソル名を指定します。
7
上のステップ 3 ~ 6 の処理を繰り返し、取り出し対象となっている行をすべて削除し
ます。
たとえば、次は、CITIES テーブルに格納されている行のうち、北アメリカにある都市の行
を取り出し、ユーザーが Y を入力すれば削除を実行するという内容の C のコード例です。
. . .
EXEC SQL
BEGIN DECLARE SECTION;
char cityname[26];
EXEC SQL
END DECLARE SECTION;
char response[5];
. . .
main ()
{
EXEC SQL
DECLARE DELETECITY CURSOR FOR
SELECT CITY,
6-68 埋 め 込 み S Q L ガ イ ド
データの削除
FROM CITIES
WHERE CONTINENT = 'North America';
EXEC SQL
OPEN DELETECITY;
while (!SQLCODE)
{
EXEC SQL
FETCH DELETECITY INTO :cityname;
if (SQLCODE)
{
if (SQLCODE == 100)
{
printf('Deletions complete.');
EXEC SQL
COMMIT;
EXEC SQL
CLOSE DELETECITY;
EXEC SQL
DISCONNECT ALL:
}
isc_print_sqlerr(SQLCODE, isc_status);
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT ALL;
exit(1);
}
printf("¥nDelete %s (Y/N)?", cityname);
gets(response);
if(response[0] == 'Y' || response == 'y')
{
EXEC SQL
DELETE FROM CITIES
WHERE CURRENT OF DELETECITY;
if(SQLCODE && (SQLCODE != 100))
{
isc_print_sqlerr(SQLCODE, isc_status);
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT;
exit(1);
}
}
}
第 6 章 データ処理
6-69
ビューを使った削除
既存の行はビューを介して削除することもできます。ただし、次の条件が必要です。
• ビューが更新可能であること。更新可能なビューの詳細は、『データ定義ガイド』を参照
してください。
• 使用するビューの DELETE 特権がユーザーに与えられていること。ストアドプロシー
ジャの場合は、そのストアドプロシージャに対して、使用するビューの DELETE 特権が
与えられていること
たとえば、DEPARTMENT テーブルの行のうち、BUDGET 列の値(予算)が 1000000 ド
ル未満の行を PART_DEPT ビューを使ってすべて削除する場合、DELETE 文は次のよう
になります。
EXEC SQL
DELETE FROM PART_DEPT
WHERE BUDGET < 1000000;
ビューの作成の詳細は、第 5 章「データ定義文」を参照してください。CREATE VIEW 構
文の詳細は、『言語リファレンス』を参照してください。
メモ
トリガーを使用して、更新不可のビューを削除する方法についてのヒントは、
『データ定義
ガイド』の「トリガーの操作」を参照してください。
DELETE でのトランザクション名の指定
InterBase では、SQL アプリケーションが複数のトランザクションを同時に実行できます。
ただし、次の条件が必要です。
• トランザクションがそれぞれ SET TRANSACTION 文で事前に宣言されていること。ト
ランザクション処理と命名の詳細は、第 4 章「トランザクションの操作」を参照してくだ
さい。
• データ操作文(SELECT、INSERT、UPDATE、DELETE、DECLARE、OPEN、FETCH、
CLOSE)で TRANSACTION 句が使用されており、関連するトランザクションが指定さ
れていること。
• SQL 文が DSQL ではないこと。DSQL では、ユーザーがトランザクション名を定義するこ
とはできません。
上 記 の よ う に、TRANSACTION 句でトランザクション名を指定することによって、
DELETE 文で任意のトランザクションを使った削除が可能になります。この場合、構文は
次のようになります。
DELETE TRANSACTION name FROM table ...
なお、最初から最後まで 1 つのトランザクションしか使用しないプログラム、または一度
に 1 つのトランザクションしか起動しないプログラムでは、TRANSACTION 句は記述し
なくてもかまいません。つまり、TRANSACTION 句は、同時に複数のトランザクション
を使用するプログラムに限って必要になります。たとえば、次の DELETE 文は、T1 とい
うトランザクションとともに機能します。
EXEC SQL
DELETE TRANSACTION T1 FROM PART_DEPT
6-70 埋 め 込 み S Q L ガ イ ド
データの削除
WHERE BUDGET < 1000000;
第 6 章 データ処理
6-71
データの削除
6-72 埋 め 込 み S Q L ガ イ ド
第
章
日付と時刻
第7章
ホスト言語の多くは、DATE 、TIME、TIMESTAMP データ型をサポートしておらず、文
字列または構造体として扱っています。InterBase では、DATE と TIME データ型は 1 つの
long 整数として、TIMESTAMP データ型は 2 つの long 整数としてデータベースに格納さ
れます。InterBase の DATE データ型には日付(年、月、日)の情報、TIME データ型には
時刻の情報、TIMESTAMP データ型には日付と時刻の両方の情報が格納されます。
この章では、SQL アプリケーションでテーブルの日付と時刻のデータに対して SELECT、
INSERT、UPDATE を使用する方法について説明します。このような操作には、以下の isc
呼び出しインターフェースルーチンを使用します。
• isc_decode_sql_date() は、InterBase の内部日付形式を C の時刻構造体(struc tm)に変換
します。
• isc_encode_sql_date() は、C の時刻構造体を InterBase の内部日付形式に変換します。
• isc_decode_sql_time() は、InterBase の内部時刻形式を C の時刻構造体に変換します。
• isc_encode_sql_time() は、C の時刻構造体を InterBase の内部時刻形式に変換します。
• isc_decode_timestamp() は、InterBase の内部タイムスタンプ形式を C の時刻構造体に変換
します。これは、以前は isc_decode_date() が行っていました。
• isc_encode_timestamp() は、C の時刻構造体を InterBase の内部タイムスタンプ形式に変換
します。これは、以前は isc_encode_date() が行っていました。
各関数の説明は、『API ガイド』を参照してください。
この章ではまた、CAST() 関数の使い方についても説明します。この CAST() 関数では、
DATE、TIME、TIMESTAMP データ型の相互の変換、および CHAR データ型との相互
の変換が可能です。また、日付リテラル(YESTERDAY、TOMORROW、NOW、TODAY)
による日付の選択と挿入についても紹介します。
第 7 章 日付と時刻
7-1
現在の日付と時刻の情報をデータベースに問い合わせる
現在の日付と時刻の情報をデータベースに問い合わせる
InterBase は、現在の日付および時刻値を取得するための定義済み SQL 関数演算子と、日
付値または時刻値を個別に取得するための EXTRACT() 関数を提供しています。
現在の日付と時刻の取得
CURRENT_DATE、CURRENT_TIME、および CURRENT_TIMESTAMP 関数演算子は、
サーバーのクロックと時間帯を使用して、SQL 文が実行された時点の日付と時刻の値を返しま
す。 単一の SQL 文では、その文内の CURRENT_DATE、CURRENT_TIME、および
CURRENT_TIMESTAMP には同じ値が使用されます。これは、次の文のように複数の行が
更新される場合に、各データ行の aTime 列が同じ値になることを意味します。
UPDATE aTable SET aTime = CURRENT_TIME;
同様に、リモートプロトコルを通した取り出しで行のバッファリングが発生した場合、
CURRENT_TIME はデータベースエンジンからカーソルを OPEN した時刻に基づき、ク
ライアントに到着した時刻には基づきません。
ド メ イ ン ま た は 列 の 定 義 の デ フ ォ ル ト の 句 と し て、CURRENT_DATE、
CURRENT_TIME、または CURRENT_TIMESTAMP を指定できます。
日付時刻情報の取り出し
EXTRACT() 関数は、データベースから日付および時刻情報を取り出します。EXTRACT()
の構文は次のとおりです。
EXTRACT (part FROM value)
EXTRACT() 式に渡される値は、DATE、TIME、または TIMESTAMP でなければなりま
せん。データ型の中に存在しない部分を取り出そうとすると、エラーになります。例を示し
ます。
EXTRACT (TIME FROM aTime)
これは正しく動作しますが、
EXTRACT (YEAR from aTIME)
これはエラーになります。
EXTRACT() 式のデータ型は、取り出される部分によって決まります。
表 7.1
日付時刻情報の取り出し
取り出す部分
結果のデータ型
表す内容
YEAR
SMALLINT
年(0 ~ 5400)
MONTH
SMALLINT
月(0 ~ 12)
DAY
SMALLINT
日(1 ~ 31)
HOUR
SMALLINT
時(0 ~ 23)
7-2 埋 め 込 み S Q L ガ イ ド
日付と時刻の取り出し
表 7.1
日付時刻情報の取り出し
取り出す部分
結果のデータ型
表す内容
MINUTE
SMALLINT
分(0 ~ 59)
SECOND
DECIMAL(6,4)
秒(0 ~ 59.9999)
WEEKDAY
SMALLINT
曜日(0 ~ 6。
0 = 日曜、1 = 月曜、...)
YEARDAY
SMALLINT
年初からの日数(1 ~ 366)
日付と時刻の取り出し
日付と時刻(タイムスタンプ)を取り出して C 言語プログラムで使用可能な形式に変換す
る手順は次のとおりです。
1 C の時刻構造体を表すホスト言語変数を作成します。ほとんどの C / C++ コンパイラで
は、C の時刻構造体は time.h ヘッダーファイルで tm 構造体型として typedef 宣言され
ています。次の C コードは、time.h をインクルードして、tm 構造体型の変数を宣言し
ています。
#include <time.h>;
. . .
struct tm hire_time;
. . .
C/C++ 以外の言語では、時刻構造体の作成方法が上記とは異なります。詳細は、使用
する言語のリファレンスマニュアルを参照してください。
2 ISC_TIMESTAMP 型の変数を宣言します。たとえば、記述は次のようになります。
ISC_TIMESTAMP hire_date;
ISC_TIMESTAMP 構造体は、gpre で前処理を行ったときに自動的に宣言されますが、
プログラムの記述の際は、実際に使用する ISC_TIMESTAMP 型の変数はプログラムで
宣言しなければなりません。
3
テーブルから ISC_TIMESTAMP 変数にタイムスタンプを取り出します。次に例を示し
ます。
EXEC SQL
SELECT LAST_NAME, FIRST_NAME, DATE_OF_HIRE
INTO :lname, :fname, :hire_date
FROM EMPLOYEE
WHERE LAST_NAME = 'Smith' AND FIRST_NAME = 'Margaret';
この後、InterBase の isc_decode_timestamp() 関数を使用して、変数 hire_date のデー
タを C プログラムで使用可能な形式に変換します。なお、この関数は、gpre による前
処理の際に自動的に宣言されます。isc_decode_timestamp() 関数では、
ISC_TIMESTAMP 型の変数(変換元)と tm 構造体型の変数(変換先)の 2 つのパラ
メータを順に指定します。たとえば、次の例は、hire_date を hire_time に変換する場合
の記述です。
第 7 章 日付と時刻
7-3
入力のための日付の書式
isc_decode_timestamp(&hire_date, &hire_time);
入力のための日付の書式
DATE データ型として入力される日付は、以下の書式を設定できます。
• YYYYpMMpDD
• MMpDDpYYYY
• DDpMMpYYYY
• YYpMMpDD
• MMpDDpYY
• DDpMMpYY
ここで
• DD = 1 桁または 2 桁の日
• MM = 1 桁または 2 桁の月、または 3 文字の月の省略名、または英語の完全な月名(大文
字と小文字の区別はなし)
• YY = 年の下 2 桁
• YYYY = 4 桁の年
• p = ASCII 区切り文字。余分な空白(タブまたはスペース)は無視される
以下の制限が適用されます。
• 「年 - 月 - 日」形式では、年は常に 4 桁でなければならない
• 「月 - 日 - 年」形式では、年は 2 桁または 4 桁。年を 2 桁で入力した場合、InterBase は
「スライドウィンドウ」アルゴリズムを使用して、年に世紀を割り当てます。詳細は、
string_to_datetime() ルーチンの説明を参照してください。
• ピリオドを区切りにし、年が最後に来るすべて数字の形式を使用した場合、InterBase はそ
の形式が「日 - 月 - 年」であると解釈します。たとえば、‘12.04.2002’ は、‘December 4,
2002’ ではなく、“April 12, 2002,” と解釈されます。
InterBase エンジンの string_to_datetime() ルーチンからの抜粋:
*
*
*
*
*
*
*
*
*
*
*
*
String must be formed using ASCII characters only.
Conversion routine can handle the following input formats
“now” current date and time
“today” Today’s date0:0:0.0 time
“tomorrow”Tomorrow’s date0:0:0.0 time
“Yesterday”Yesterday’s date0:0:0.0 time
YYYY-MM-DD [HH:[Min:[SS.[Thou]]]]]
MM:DD[:YY [HH:[Min:[SS.[Thou]]]]]
DD:MM[:YY [HH:[Min:[SS.[Thou]]]]]
Where:
DD = 1..31(Day of month)
YY = 00..99 2-digit years are converted to the nearest year
7-4 埋 め 込 み S Q L ガ イ ド
日付と時刻の挿入
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
in a 50-year range. Eg: if this is 1996:
96 ==> 1996
97 ==> 1997
...
00 ==> 2000
01 ==> 2001
...
44 ==> 2044
45 ==> 2045
46 ==> 1946
47 ==> 1947
...
95 ==> 1995
If the current year is 1997, then 46 is converted
to 2046 (etc.)
= 100.. 5200
MM = 1 .. 12 (Month of year)
= “JANUARY”...(etc.)
HH = 0...23 (Hour of day)
Min = 0...59 (Minute of hour)
SS = 0...59 (Second of minute - LEAP second not supported)
Thou = 0...9999 (Fraction of second)
HH, Min, SS, Thou default to 0 if missing.
YY defaults to current year if missing.
Note: ANY punctuation can be used instead of : (eg: / - etc)
Using .(period) in either of the first two separation
points will cause the date to be parsed in European DMY
format.
Arbitrary whitespace (space or TAB) can occur between
components.
日付と時刻の挿入
テーブルに日付と時刻(タイムスタンプ)を挿入する場合、日付をホスト言語の形式から
InterBase の形式に変換する必要があります。C 言語のプログラムでの日付の変換および
テーブルへの挿入は、次の手順で行います。
1 C 言語の時刻構造体を表す変数を作成します。通常、C または C++ のコンパイラでは、
C の時刻構造体は time.h ヘッダーファイルで tm 構造体型として typedef 宣言されてい
ます。次の C コードは、time.h をインクルードし、tm 構造体型の変数 hire_time を宣
言しています。
#include <time.h>;
. . .
struct tm hire_time;
. . .
第 7 章 日付と時刻
7-5
日付と時刻の更新
C/C++ 以外の言語では、時刻構造体の作成方法が上記とは異なります。詳細は、使用
する言語のリファレンスマニュアルを参照してください。
2 InterBase が使用する ISC_TIMESTAMP 型のホスト変数を宣言します。ホスト変数の
宣言は、たとえば、次のようになります。
ISC_TIMESTAMP mydate;
ISC_TIMESTAMP 構造体は、gpre で前処理を行ったときに自動的に宣言されますが、
プログラムの記述の際は、実際に使用する ISC_TIMESTAMP 型の変数はプログラム
で宣言しなければなりません。
3 日付情報を hire_time に格納します。
4 InterBase の isc_encode_timestamp() 関数を使用して、変数 hire_time のデータを
InterBase の内部形式に変換するとともに、データを ISC_TIMESTAMP 型の変数(こ
こでは hire_date)に格納します。この関数は、gpre による前処理の際に自動的に宣言
されます。isc_encode_timestamp() 関数では、tm 型の変数(変換元)と
ISC_TIMESTAMP 型の変数(変換先)の 2 つのパラメータを順に指定します。
たとえば、次のコードは、hire_time を hire_date に変換します。
isc_encode_timestamp(&hire_time, &hire_date);
5
日付をテーブルに挿入します。次に例を示します。
EXEC SQL
INSERT INTO EMPLOYEE (EMP_NO, DEPARTMENT, DATE_OF_HIRE)
VALUES (:emp_no, :deptname, :hire_date);
日付と時刻の更新
テーブルの DATE、TIME、または TIMESTAMP データ型を更新するには、ホスト言語の
形式を InterBase の形式に変換してから格納します。ホスト変数を InterBase 形式に変換す
る方法は、7-4 ページの「入力のための日付の書式」を参照してください。実際の更新は、
UPDATE 文を使って行います。次に例を示します。
EXEC SQL
UPDATE EMPLOYEE
SET DATE_OF_HIRE = :hire_date
WHERE DATE_OF_HIRE < '1 JAN 1994'
CAST() による日付と時刻の変換
SELECT 文で組み込みの CAST() 関数を使用すると、日付および時刻データ型と、文字
ベースのデータ型の間で以下の変換を行うことができます。
• DATE、TIME、TIMESTAMP データ型から CHAR データ型へ
文字データ型は、少なくとも 24 文字の長さが必要です。ただし、TIMESTAMP を DATE
にキャストすれば、その DATE は 24 文字より短い CHAR にキャストできます。次に例を
示します。
7-6 埋 め 込 み S Q L ガ イ ド
CAST() による日付と時刻の変換
SELECT CAST (CAST (timestamp_col AS DATE) AS CHAR(10)) FROM table1;
• CHAR データ型から DATE、TIME、TIMESTAMP データ型へ
• DATE、TIME データ型から TIMESTAMP データ型へ
• TIMESTAMP データ型から DATE、TIME データ型へ
日 付 ま た は 時 刻 デ ー タ 型 は、BLOB、SMALLINT、INTEGER、FLOAT、DOUBLE
PRECISION、NUMERIC、および DECIMAL データ型との間でキャストすることはでき
ません。
通常、CAST() は WHERE 句で異なるデータ型を比較するために使用します。CAST() の
構文は次のとおりです。
CAST (<value> AS <datatype>)
次の WHERE 句では、CAST() を使って CHAR データ型である INTERVIEW_DATE の
データを DATE データ型に変換し、その後、DATE データ型である HIRE_DATE のデー
タと比較しています。
… WHERE HIRE_DATE = CAST(INTERVIEW_DATE AS DATE);
次の例では、CAST() で DATE データ型を CHAR データ型に変換しています。
… WHERE CAST(HIRE_DATE AS CHAR) = INTERVIEW_DATE;
CAST() を使用することで、同じテーブルにデータ型の異なる列があっても比較が可能で
す。また、CAST() は、別のテーブルの列に対しても使用できます。
次の 2 つのセクションでは、日付時刻データ型(DATE、TIME、TIMESTAMP)と他の
SQL データ型との間で行うことができる変換について説明します。
CAST() の詳細は、第 6 章「データ処理」を参照してください。
SQL データ型から日付時刻データ型へのキャスト
次の表は、DATE、TIME、TIMESTAMP データ型にキャストできる SQL データ型を示
しています。
表 7.2
SQL データ型から日付時刻データ型へのキャスト
キャスト先
キャスト元
TIMESTAMP
DATE
TIME
SMALLINT
INTEGER
FLOAT
DOUBLE PRECISION
NUMERIC
DECIMAL
エラー
エラー
エラー
VARCHAR(n)
CHAR(n)
CSTRING(n)
文字列が次の形式の場合、 文字列が次の形式の場合、 文字列が次の形式の場合、
キャスト可。
キャスト可。
キャスト可。
BLOB
YYYY-MM-DD
HH:MM:SS.thou
YYYY-MM-DD
HH:MM:SS.thou
エラー
エラー
エラー
第 7 章 日付と時刻
7-7
CAST() による日付と時刻の変換
表 7.2
SQL データ型から日付時刻データ型へのキャスト ( 続き )
キャスト先
キャスト元
TIMESTAMP
DATE
TIME
TIMESTAMP
常にキャスト可
キャスト可。TIMESTAMP
の日付部分
キャスト可。
常にキャスト可
エラー
エラー
常にキャスト可
DATE
キャスト可。
TIMESTAMP の時刻部分
TIMESTAMP の時刻部分
は 0:0:0.0000 に設定され
る
TIME
キャスト可。
TIMESTAMP の日付部分
は起算日付(1858 年 11 月
17 日)に設定される
日付時刻データ型から他の SQL データ型へのキャスト
次の表は、DATE、TIME、TIMESTAMP データ型からキャストできる SQL データ型を
示しています。
表 7.3
日付時刻データ型から他の SQL データ型へのキャスト
キャスト元
キャスト先
TIMESTAMP
DATE
TIME
SMALLINT
INTEGER
FLOAT
DOUBLE PRECISION
NUMERIC
DECIMAL
エラー
エラー
エラー
VARCHAR(n)
CHAR(n)
CSTRING(n)
n が 24 文字以上の場合は
キャスト可。結果の文字列
は次の形式になる。
n が 10 文字以上の場合は
キャスト可。結果の文字列は
次の形式になる。
n が 10 文字以上の場合は
YYYY-MM-DD
HH:MM:SS.thou
YYYY-MM-DD
HH:MM:SS.thou
BLOB
エラー
エラー
エラー
TIMESTAMP
常にキャスト可
キ ャ ス ト 可。時 刻 部 分 は
0:0:0.0000 に設定される
1858 年 11 月 17 日に設定さ
キャスト可。結果の文字列は
次の形式になる。
キ ャ ス ト 可。日 付 部 分 は
れる
DATE
キャスト可。
常にキャスト可
エラー
エラー
常にキャスト可
TIMESTAMP の日付部分
が結果になる
TIME
キャスト可。
TIMESTAMP の時刻部分
が結果になる
7-8 埋 め 込 み S Q L ガ イ ド
日付リテラルの使い方
DATE を文字列にキャストすると "YYYY-MM-DD" の形式(“MM” は 2 桁の月)になり
ます。結果が文字列変数のサイズでは収まらない場合、文字列切り詰めの例外が生成されま
す。
文字列を日付にキャストする場合、文字列は次の形式でなければなりません。
‘yyy-mm-dd’‘yyyy/mm/dd’‘yyyy mm dd
‘yyyy:mm:dd’‘yyyy.mm.dd’
この 4 つの形式では、月(mm)は完全な月名または 3 文字の省略名に置き換えることが
できます。ただし、順序は 4 桁の年、月、日の順でなければなりません。
また、次の形式も使用できます。
‘mm-dd-yy’‘mm-dd-yyyy’‘mm/dd/yy’‘mm/dd/yyyy’
‘mm dd yy’‘mm dd yyyy’‘mm:dd:yy’‘mm:dd:yyyy’
‘dd.mm.yy’‘dd.mm.yyyy’
年が 2 桁の日付を入力した場合、InterBase は「スライドウィンドウ」アルゴリズムを使っ
て年に世紀を割り当てます。
月を英語の完全な名前または英語の 3 文字の省略名で指定する場合、月と日はどちらが先
でもかまいません。以下の例では、“xxx” は完全な月名または 3 文字の省略名を表します。
以下のすべての形式を使用できます。
‘dd-xxx-yy’‘dd-xxx-yyyy’‘xxx-dd-yy’‘xxx-dd-yyyy’
‘dd xxx yy’‘dd xxx yyyy’‘xxx dd yy’‘xxx dd yyyy’
‘dd:xxx:yy’‘dd:xxx:yyyy’‘xxx:dd:yy’‘xxx:dd:yyyy’
たとえば、以下の INSERT 文はどれも 1943 年 1 月 22 日の日付を挿入します。
INSERT
INSERT
INSERT
INSERT
INTO
INTO
INTO
INTO
t1
t1
t1
t1
VALUES
VALUES
VALUES
VALUES
(‘1943-01-22’);
(‘01/22/1943’);
(‘22.01.1943’);
(‘jan 22 1943’);
次の文は、2043 年 1 月 22 日の日付を挿入します。
INSERT INTO t1 VALUES (‘01/22/43’);
日付リテラルの使い方
InterBase では、'NOW'、‘TODAY’、'YESTERDAY'、および 'TOMORROW' の 4 つの日
付リテラルを使用できます。日付リテラル は引用符で囲まれた文字列値で、その値は
EXTRACT、SELECT、INSERT、および UPDATE
で日付として解釈されます。
'TIMESTAMP' は、その時点の日付と時刻を InterBase 形式で表す日付リテラルです。
'DATE' は、当 日 の 日 付 情 報を表し、時刻は 0 です。同じように、'YESTERDAY' と
'TOMORROW' は名前どおりの日付を表し、時刻は 0 です。
EXTRACT と SELECT では、'TODAY' と 'NOW' を WHERE 句の検索条件で使用するこ
とで、取り出すデータの絞り込みが可能になります。例を示します。
EXEC SQL
SELECT * FROM CROSS_RATE WHERE UPDATE_DATE = 'NOW';
第 7 章 日付と時刻
7-9
日付および時刻データ型の加算と減算
INSERT または UPDATE で 'TODAY' と 'NOW' を使用すれば、isc 呼び出しで C 形式を
InterBase 形式に変換しなくても、日付時刻情報を入力することができます。
EXEC SQL
INSERT INTO CROSS_RATE VALUES(:from, :to, :rate, 'NOW');
EXEC SQL
UPDATE CROSS_RATE
SET CONV_RATE = 1.75,
SET UPDATE_DATE = 'TODAY'
WHERE FROM_CURRENCY = 'POUND' AND TO_CURRENCT = 'DOLLAR'
AND UPDATE_DATE < 'TODAY';
日付および時刻データ型の加算と減算
次の表に、DATE、TIME、TIMESTAMP、および数値データ型の加算と減算の結果を示
します。「数値」は、データベースエンジンによって高精度数値にキャストが可能なすべて
の型(INTEGER、DECIMAL、NUMERIC など)を表します。
表 7.4
日付 / 時刻データ型の加算と減算
Operand1
演算子
Operand2
結果
DATE
+
DATE
エラー
DATE
+
TIME
TIMESTAMP(連結)
DATE
+
TIMESTAMP
エラー
DATE
+
数値
DATE + 日数(小数部は無視される)
TIME
+
DATE
TIMESTAMP(連結)
TIME
+
TIME
エラー
TIME
+
TIMESTAMP
エラー
TIME
+
数値
TIME + 秒数(24 時間の剰余演算)
TIMESTAMP +
DATE
エラー
TIMESTAMP +
TIME
エラー
TIMESTAMP +
TIMESTAMP
エラー
TIMESTAMP +
数値
TIMESTAMP(DATE + 日数)
TIME + 数値の小数部を秒数に変換した値
DATE
-
DATE
DECIMAL(9,0)(日数を表す)
DATE
-
TIME
エラー
DATE
-
TIMESTAMP
エラー
DATE
-
数値
DATE= 日数(小数部は無視される)
7-10 埋 め 込 み S Q L ガ イ ド
日付と時刻の比較
表 7.4
日付 / 時刻データ型の加算と減算 ( 続き )
Operand1
演算子
Operand2
結果
TIME
-
DATE
エラー
TIME
-
TIME
DECIMAL(9,4)(秒数を表す)
TIME
-
TIMESTAMP
エラー
TIME
-
数値
TIME - 秒数 (24 時間の剰余演算)
TIMESTAMP -
DATE
エラー
TIMESTAMP -
TIME
エラー
TIMESTAMP -
TIMESTAMP
DECIMAL(18,9)(整数部が日数、小数部が秒数を表
す)
TIMESTAMP -
数値
TIMESTAMP:DATE - 日数、および TIME - 数値の
小数部を秒数に変換した値
日付と時刻の比較
日付および時刻値は、暗黙的に変換される場合があります。次の比較を見てください。
Table1.SomeDateField <= ‘12/31/1999’
InterBase は、比較演算が行われる場合、文字列リテラル ‘12/31/1999’ を自動的に DATE
に変換します。
ただし、式がそのままで意味を持つ場合は、暗黙の変換は必ずしも行われません。次に例
を示します。
‘31.5.2000’ < ‘1.6.2000’
この 2 つの文字列のアルファベット順の比較は偽になるため、この式は常に偽になります。
i この 2 つの値の文字列比較は有効なので、これらが「日付のように」見えても、日付への
暗黙の変換は行われません。一方、次の比較を見てください。
CAST(‘31.5.2000’ AS DATE) < CAST(‘1.6.2000’ AS DATE)
これらの文字列に対応する日付を比較した結果は真なので、この式は真になります。詳細
は、『データ定義ガイド』の「暗黙の型変換」を参照してください。
日付および時刻データ型を集計関数で使用する
日付および時刻データ型は、MIN()、MAX()、COUNT() 関数、およびこれらの関数の
DISTINCT 引数、また SELECT() 関数の GROUP BY 引数に使用できます。日付および時
刻データ型を SUM() および AVG() 関数で使用するとエラーが返されます。
第 7 章 日付と時刻
7-11
7-12 埋 め 込 み S Q L ガ イ ド
第
第8章
章
BLOB データの操作
この章では、BLOB データ型とそのサブタイプ、BLOB の格納方法、SQL、DSQL、およ
び API 呼び出しを使って BLOB にアクセスする方法、BLOB のフィルタリングについて
説明します。また、BLOB フィルタの記述方法についても説明します。
BLOB の概要
BLOB とは、動的にサイズを決定できるデータ型を指します。この BLOB データでは、サ
イズおよびコード化形式は指定されません。BLOB を使用すると、さまざまな型の大容量
データを格納できます。種類としては主に以下の 4 つがあります。
• ビットマップイメージ
• ベクタードローイング
• サウンド、ビデオセグメント(ビデオクリップ)、およびその他のマルチメディア情報
• テキスト(文字データ)
BLOB データ型で格納されたデータは、他のデータベースに格納されているデータとほとん
ど同じように操作できます。また、InterBase では、BLOB データはデータベースに直接格納
されます。これに対して、一般のシステムでは、データベースにはポインタだけが置かれ、
実際のファイルはデータベースの外部に配置されるのが一般的です。InterBase には、さらに
識別ハンドルを記述したテーブルがあり、この識別ハンドルによってデータベース中の
BLOB データの位置が示されます。このほか、BLOB データがデータベースに直接格納され
ていることから、データへのアクセス、データの管理の両面で動作が大幅に向上します。
以上のように、各種の BLOB データ型をサポートしていることに加えて、BLOB データに
対する管理能力も優れているため、InterBase は、トランザクション中心型のマルチメディ
アアプリケーションの作成にも適しています。たとえば、対話形式のキオスク型のアプリ
ケーションの作成には理想的で、数百、数千の製品について、説明や写真、ビデオクリッ
プを盛り込むことができます。また、これらのアプリケーションに、POS システムや受発
注システムを組み込むこともできます。
第 8 章 BLOB データの操作
8-1
BLOB データの格納方法
BLOB データの格納方法
BLOB データは、種類としてはビットマップイメージ、サウンド、ビデオ、テキストの 4
つに分かれます。ただし、この 4 つは内容から見た場合の種類で、データベースへの BLOB
データの格納に先立ち、現在使用しているプラットフォームやシステムに対応したファイ
ル形式で BLOB データを用意しておかなければなりません。この場合のファイルとして
は、次のようなものがあります。
• TIFF、PICT、.BMP、.WMF、.GEM、TARGA などのビットマップまたはベクターグラ
フィックファイル
• MIDI または .WAV のサウンドファイル
• Video for Windows のファイル(.AVI)や QuickTime ムービーのファイル(.MOV)
• ASCII、.MIF、.DOC、.WPx などのテキストファイル
• CAD ファイル
上記の各種ファイルは、プログラムを使ってメモリからデータベースにロードしなければ
なりません。処理の手順は、ホスト言語のデータやレコードを InterBase に格納する場合と
同じです。
BLOB サブタイプ
BLOB データの扱い方は、InterBase の他のデータ型のデータの場合と同じです。ただし、
BLOB データの場合、データ型の検査はそれほど厳密には行われません。これは、BLOB
データとしてユーザーが定義できるデータ型が非常に多いためです。ユーザーはデータ型
の検査がゆるやかな分だけ、自由にデータ型を定義することができます。このユーザー定
義の BLOB データ型を BLOB のサブタイプと呼んでいます。また、ユーザー定義のサブタ
イプとは別に、標準のサブタイプが事前に用意されています。InterBase で用意されている
標準サブタイプは次の 7 つです。
表 8.1
InterBase で定義されている BLOB サブタイプ
BLOB
サブタイプ
説明
0
構造を持たないバイナリデータ一般、または型が未決定のデータに適用される
1
テキスト
2
バイナリ言語表現(BLR)
3
アクセス制御リスト
4
(予約)
5
テーブルの現在のメタデータの符号化記述
6
正常に終了しなかったマルチデータベーストランザクションの記述
ユーザー定義のサブタイプは、–1 ~ –32768 までの負の数値として指定します。正の数値
は InterBase で予約されているため、ユーザー定義のサブタイプには使用できません。
8-2 埋 め 込 み S Q L ガ イ ド
BLOB データの格納方法
たとえば、次の文は、テーブルの作成の際に、3 つの BLOB 列を定義します。つまり、
BLOB1 というサブタイプ 0(デフォルト)の BLOB 列、BLOB2 というサブタイプ 1
(TEXT)の BLOB 列、BLOB3 というサブタイプ –1(ユーザー定義)の BLOB 列を定義
します。
EXEC SQL CREATE TABLE TABLE2
(
BLOB1 BLOB,
BLOB2 BLOB SUB_TYPE 1,
BLOB3 BLOB SUB_TYPE -1
);
また、BLOB 定義の際は、サブタイプと同時にデフォルトのセグメント長を定義すること
もできます。セグメント長を定義するときは、SUB_TYPE の右に SEGMENT SIZE と長
さ(バイト数)を記述します。次に例を示します。
EXEC SQL CREATE TABLE TABLE2
(
BLOB1 BLOB SUB_TYPE 1 SEGMENT SIZE 100;
);
なお、ユーザー定義のサブタイプは BLOB フィルタを使って別のユーザー定義のサブタイ
プに変換できますが、この場合、双方のサブタイプに互換性がなければなりません。互換性
のない場合は、サブタイプの整合性は保証されません。
BLOB のデータベース上での格納形式
BLOB データは、バイナリまたはテキストで構成されるかなり大きなオブジェクトで、サ
イズも可変です。このため、InterBase では、BLOB データはセグメントという方法を使っ
て効率よく格納されます。つまり、1 つの BLOB データは大容量データのかたまりなので、
このかたまりを 1 つとして格納した場合は、ディスク容量の効率的利用という点では不利
になります。この点を考慮して、InterBase では、BLOB データは複数のセグメントという
形で格納され、BLOB の作成の際はハンドルを使ってインデックスが作成されます。この
ハンドルを BLOB ID と呼びます。この BLOB ID は 4 ワード(64 ビット)の長さで、こ
こには、テーブルの識別子と BLOB の識別子が組み合わされた形で保持されます。BLOB
ID は、重複しないように BLOB セグメントごとに作成されます。
BLOB ID は、テーブルの所定のフィールドに格納されます。BLOB ID は BLOB データの
先頭を指すこともありますし、場合によっては、ポインタのページ(複数のポインタで構
成される表)を指すこともあります。参照先がページの場合、ページの個々のポインタが
それぞれ BLOB データを指すことになります。BLOB ID は、SELECT 文で BLOB ID を
指定することで取り出すことができます。たとえば、記述は次のようになります。
EXEC SQL
DECLARE BLOBDESC CURSOR FOR
SELECT GUIDEBOOK
FROM TOURISM
WHERE STATE = 'CA';
BLOB 列は、CREATE TABLE を使用して、BLOB 以外のデータ型の列とともに作成しま
す。
第 8 章 BLOB データの操作
8-3
BLOB データの格納方法
他のデータ型の列と併せて PROJ_DESC という BLOB 列を作成する場合、CREATE
TABLE 文の記述は次のようになります。ここでは、サブタイプのパラメータは 1 になってい
ます。これは TEXT BLOB を指します。さらに、セグメント長としては 80 バイトが設定さ
れています。
CREATE TABLE PROJECT
(
PROJ_ID PROJNO NOT NULL,
PROJ_NAME VARCHAR(20) NOT NULL UNIQUE,
PROJ_DESC BLOB SUB_TYPE 1 SEGMENT SIZE 80,
TEAM_LEADER EMPNO,
PRODUCT PRODTYPE,
...
);
次の図は、BLOB ID を格納している BLOB 列と、その BLOB ID により参照される BLOB
データとの関係を示したものです。
図 8.1
データベース中の BLOB ID と BLOB セグメントとの関連
BLOB
列
テーブル行
…
BLOB ID
BLOB データ
…
セグメント セグメント セグメント
…
このように、InterBase では、テーブルの行に BLOB ID が置かれています。直接 BLOB
データがテーブルに格納されるわけではありません。つまり、BLOB ID は BLOB データ
の最初のセグメントを指すポインタで、実際の BLOB データはデータベースの別の場所に
セグメントの連続という形で格納されます。また、アプリケーションで BLOB の書き込み
を行うときは、セグメントごとに書き込みを行うことになります。同じく、アプリケーショ
ンで BLOB の読み取りを行う場合、その読み取りはセグメント単位で実行することになり
ます。この場合、BLOB データは一般に大きいため、アプリケーションコードでループを
使って処理するのが普通です。
BLOB のセグメント長
CREATE TABLE を使って BLOB 列を定義する場合、BLOB 定義文の列に書き込まれる
BLOB のセグメント長を指定します。セグメント長は、セグメントの最大バイト数を指定
します。これで、その列については、指定されたバイト数をセグメントの最大長として
BLOB データの書き込みと読み取りが行われることになります。なお、セグメントのデ
フォルトの長さは 80 バイトとなっています。たとえば、次の例は、セグメント長が 120 バ
イトの BLOB 列を作成する場合です。
EXEC SQL CREATE TABLE TABLE2
(
Blob1 Blob SEGMENT SIZE 120;
8-4 埋 め 込 み S Q L ガ イ ド
SQL での BLOB データへのアクセス
);
テーブルの作成時にセグメント長を設定した場合(設定しないときはデフォルトの 80)、そ
のセグメント長にしたがって内部バッファの大きさが決められ、このバッファに BLOB
データのセグメントが書き込まれることになります。通常は、テーブルで設定されているセ
グメント長を超えるような書き込みは行わないようにします。この場合、バッファのオー
バーフローが発生してメモリの整合性が失われることがあります。
テーブルの作成時に設定した列のセグメント長は、BLOB データの処理に関する文では、
変数を使って指定します。これで、そのセグメントの長さを最大長として BLOB データの
処理が実行されます。たとえば、SELECT、INSERT、UPDATE の場合、変数で指定した
セグメントを単位として読み取りや書き込みが行われることになります。
たとえば、次は INSERT CURSOR 文の例です。ここでは、変数 segment_length を使って
セグメント長を指定しています。
EXEC SQL
INSERT CURSOR BCINS VALUES (:write_segment_buffer
INDICATOR :segment_length);
INSERT CURSOR の構文およびセグメント長の指定については、『言語リファレンス』を
参照してください。
DECLARE CURSOR でのセグメント長の変更
DECLARE CURSOR 文では、オプションの MAXIMUM_SEGMENT を記述することに
より、テーブルの作成時に設定されている BLOB 列のセグメント長を変更することができ
ます。たとえば、次の BLOB INSERT カーソル宣言では、BLOB2 という BLOB 列に設定
されているセグメント長を 1024 バイトに拡大しています。
EXEC SQL
DECLARE BCINS CURSOR FOR INSERT Blob Blob2 INTO TABLE 2
MAXIMUM_SEGMENT 1024;
メモ
MAXIMUM_SEGMENT を使ってセグメント長を変更した場合、その変更は、
DECLARE CURSOR で宣言したカーソルに対してだけ有効です。したがって、ここで宣言
したカーソル以外のカーソルでは、テーブルの作成時に設定されたセグメントサイズがその
まま使用されます。このようなカーソルに対して、個別に MAXIMUM_SEGMENT を使っ
てセグメント長を変更できます。
上のようにしてセグメント長を変更したときでも、InterBase の動作効率が低下することは
ありません。そのため、状況によってセグメント長は任意に指定できます。セグメントの最
大長は 64KB(65,535 バイト)です。
SQL での BLOB データへのアクセス
InterBase では、SELECT、INSERT、UPDATE、DELETE を使用して、BLOB データの
取り出し、挿入、更新、削除が可能です。以降、こういった処理の手順をサンプルプログラ
ムを使って説明します。どのサンプルプログラムも、SQL での BLOB データ処理としては
一般に使用されるものです。
第 8 章 BLOB データの操作
8-5
SQL での BLOB データへのアクセス
BLOB データの取り出し
サンプルプログラムを例に、BLOB データを取り出す場合の手順を説明します。ここでは、
TOURISM テーブルの GUIDEBOOK 列から BLOB データを取り出します。
1
次のようにして変数を宣言します。それぞれ、BLOB ID、BLOB データ、STATE 列
のデータ、セグメント長を格納するための変数です。
EXEC SQL
BEGIN DECLARE SECTION;
BASED ON TOURISM.GUIDEBOOK blob_id;
BASED ON TOURISM.GUIDEBOOK.SEGMENT blob_segment_buf;
BASED ON TOURISM.STATE state;
unsigned short blob_seg_len;
EXEC SQL
END DECLARE SECTION;
上の文で、BASED ON … SEGMENT 構文は、データが BLOB の場合に必要になり
ます。ここでは、変数 blob_segment_buf を宣言しています。FETCH の処理では、こ
の変数に BLOB データのセグメント(1 つ)が格納されることになります。BASED
ON 文の詳細は、『言語リファレンス』を参照してください。
2 BLOB 列のデータを取り出すためのカーソル(テーブルカーソル)を宣言します。こ
こでは SELECT で GUIDEBOOK 列を指定します。なお、STATE 列も指定していま
す。
EXEC SQL
DECLARE TC CURSOR FOR
SELECT STATE, GUIDEBOOK
FROM TOURISM
WHERE STATE = 'CA';
3 BLOB データを読み取るためのカーソルを宣言します。これは、BLOB セグメントの
読み取りに使用する特殊なカーソルで、BLOB 読み取りカーソルと呼ばれています。
EXEC SQL
DECLARE BC CURSOR FOR
READ Blob GUIDEBOOK
FROM TOURISM;
なお、GUIDEBOOK BLOB 列のセグメント長は 60 に設定されているため、BLOB
カーソル BC では、最長 60 バイトの大きさで各セグメントが読み取られることになり
ます。
GUIDEBOOK のデータベーススキーマで指定したセグメントの長さを上書きするに
は、MAXIMUM_SEGMENT オプションを使用します。たとえば、次のコードは、
BLOB 読み取り処理を最大 40 バイトに限定し、SQLCODE は 101 に設定されて、セ
グメントの一部だけが読み取られていることを示します。
EXEC SQL
DECLARE BC CURSOR FOR
READ Blob GUIDEBOOK
8-6 埋 め 込 み S Q L ガ イ ド
SQL での BLOB データへのアクセス
FROM TOURISM
MAXIMUM_SEGMENT 40;
セグメント長を変更したときでも、セグメントは必ず 1 回に 1 つずつ、順に読み取ら
れます。
4
テーブルカーソルを開き、FETCH で BLOB データ(BLOB ID)を格納している行を
取り出します。
EXEC SQL
OPEN TC;
EXEC SQL
FETCH TC INTO :state, :blob_id;
上の FETCH 文により、STATE と GUIDEBOOK の 2 つの列のデータ
(GUIDEBOOK の場合は BLOB ID)が、それぞれ state と blob_id の 2 つの変数に格
納されます。
5
変数 blob_id に格納されている BLOB ID を使用して、BLOB 読み取りカーソル(BC)
を開くとともに、BLOB データの最初のセグメントを取り出します。
EXEC SQL
OPEN BC USING :blob_id;
EXEC SQL
FETCH BC INTO :blob_segment_buf:blob_seg_len;
前の文では、FETCH が実行されると同時に、BLOB データの最初のセグメントが変数
blob_segment_buf に格納されます。この場合のセグメントの長さは、変数
blob_seg_len に入っているバイト数(セグメント長)によって決められます。
6
ループを使用して、2 番め以降のセグメントを取り出します。この場合、SQLCODE を
使って取り出しのたびに状態を確認します。SQLCODE には、BLOB データの全セグ
メントの取り出しが終わると 100 が設定されます。また、取り出されたセグメントが本
来のセグメントより短かったとき(取り出されたのが本来のセグメントの一部だった場
合)には、SQLCODE には 101 が設定されます。
while (SQLCODE != 100 || SQLCODE == 101)
{
printf("%*.*s", blob_seg_len, blob_seg_len, blob_segment_buf);
EXEC SQL
FETCH BC INTO :blob_segment_buf:blob_seg_len;
}
このように SQLCODE を記述した場合、前述のように、FETCH によって取り出され
たセグメントが本来のセグメント長に達していないと、SQLCODE にエラーコード
101 が設定されます。
たとえば、セグメントバッファの長さが 40 で、特定のセグメントの長さが 60 の場合、
最初の FETCH は、セグメントに残っているデータを示すエラーコード 101 が生成さ
れます。2 番めの FETCH は、残りの 20 バイトのデータを読み取り、SQLCODE に 0
が設定され、次のセグメントの読み取りの準備ができていることを示します。また、そ
のセグメントが BLOB データの最後のセグメントである場合は、100 が設定されます。
7 BLOB 読み取りカーソル(BC)を閉じます。
第 8 章 BLOB データの操作
8-7
SQL での BLOB データへのアクセス
EXEC SQL
CLOSE BC;
8
テーブルカーソルを閉じます。
EXEC SQL
CLOSE TC;
BLOB データの挿入
サンプルプログラムを例にして、TOURISM テーブルの GUIDEBOOK 列に BLOB デー
タを挿入する場合の手順を説明します。
1
次のようにして変数を宣言します。それぞれ、BLOB ID、BLOB データ、STATE 列
のデータ、セグメント長を格納するのための変数です。
EXEC SQL
BEGIN DECLARE SECTION;
BASED ON TOURISM.GUIDEBOOK blob_id;
BASED ON TOURISM.GUIDEBOOK.SEGMENT blob_segment_buf;
BASED ON TOURISM.STATE state;
unsigned short blob_seg_len;
EXEC SQL
END DECLARE SECTION;
• 上の文で、BASED ON … SEGMENT 構文は、データが BLOB の場合に必要にな
ります。ここでは、変数 blob_segment_buf を宣言しています。FETCH の処理で
は、この変数に BLOB データのセグメント(1 つ)が格納されることになります。
BASED ON ディレクティブの詳細は、『言語リファレンス』を参照してください。
2 BLOB データを挿入するためのカーソル(BLOB 挿入カーソル)を宣言します。
EXEC SQL
DECLARE BC CURSOR FOR INSERT Blob GUIDEBOOK INTO TOURISM;
3 BLOB 挿入カーソルを開きます。INTO の右には、BLOB ID を格納する変数を指定しま
す。
EXEC SQL
OPEN BC INTO :blob_id;
4
変数 blob_segment_buf(バッファ)にセグメントを格納し、セグメント長を計算する
とともに、INSERT CURSOR 文を使ってセグメントを書き込みます。
sprintf(blob_segment_buf, 'Exploring Napa County back roads');
blob_segment_len = strlen(blob_segment_buf);
EXEC SQL
INSERT CURSOR BC VALUES (:blob_segment_buf:blob_segment_len);
ループを使って上の処理を繰り返し、BLOB データのセグメントを全部書き込みます。
5 BLOB 挿入カーソルを閉じます。
EXEC SQL
8-8 埋 め 込 み S Q L ガ イ ド
SQL での BLOB データへのアクセス
CLOSE BC;
6 INSERT 文を使用して、TOURISM テーブルに BLOB データを含む新規の行を追加し
ます。
EXEC SQL
INSERT INTO TOURISM (STATE,GUIDEBOOK) VALUES ('CA',:blob_id);
7
変更をデータベースにコミットします。
EXEC SQL
COMMIT;
BLOB データの更新
BLOB データを直接更新することはできません。BLOB データを更新する場合、まず新規
の BLOB データを作成し(BLOB 挿入カーソルを宣言し、そのカーソルを開く)、バッファ
に既存の BLOB データを読み取ります。その BLOB データに修正を加えた後、修正済み
の BLOB データを新規の BLOB データに書き込むという操作を行います。最後に、既存
の BLOB データをこの新規の BLOB データに置き換えます。
新規の BLOB データの作成から BLOB データの置き換えまでの手順は、次のとおりです。
1 BLOB データを挿入するためのカーソル(BLOB 挿入カーソル)を宣言します。
EXEC SQL
DECLARE BC CURSOR FOR INSERT BLOB GUIDEBOOK INTO TOURISM;
2 BLOB 挿入カーソルを開きます。INTO の右には、BLOB ID を格納する変数を指定し
ます。
EXEC SQL
OPEN BC INTO :blob_id;
3
変数 blob_segment_buf(バッファ)に既存の BLOB データのセグメントを格納し、セ
グメントの長さを計算して必要な修正を行います。この後、INSERT CURSOR 文を
使ってセグメントを書き込みます。
/* 古い BLOB セグメントデータの最初 / 次のセグメントを
* blob_segment_buf; に自動的に読み取る */
EXEC SQL
INSERT CURSOR BC VALUES (:blob_segment_buf:blob_segment_len);
ループを使って上の処理を繰り返し、BLOB データのセグメントを全部書き込みます。
4 BLOB 挿入カーソルを閉じます。
EXEC SQL
CLOSE BC;
5
ここまでで新規の BLOB データの作成が完了したので、最後に UPDATE 文を使用し
て、テーブルの新規の BLOB データと既存の BLOB データを入れ替えます。記述は次
のようになります。
EXEC SQL UPDATE TOURISM
SET
第 8 章 BLOB データの操作
8-9
SQL での BLOB データへのアクセス
GUIDEBOOK = :blob_id;
WHERE CURRENT OF TC;
メモ
上の TC は宣言済みのテーブルカーソルで、このカーソルにより、テーブルの更新対象の
行が特定され、その行が取り出されるとともに更新が行われます。
BLOB データが TEXT の場合も同じ手順で更新できます。つまり、既存の BLOB テキス
トデータを変数(バッファ)に読み取り、修正した後、そのデータを UPDATE 文を使って
既存の BLOB データに上書きします。
BLOB データの削除
BLOB データの削除には、2 とおりの方法があります。1 つは、BLOB が格納されている
行自体を削除する方法です。もう 1 つは、BLOB が格納されている列に NULL、または別
の BLOB データの BLOB ID を入れる(つまり、別の BLOB データをもとに新規の BLOB
データを作成する)方法です。
NULL を 入 れ て BLOB データを削除する場合、次のように記述します。ここでは、
TOURISM テーブルの GUIDEBOOK 列に NULL が格納されます。
EXEC SQL UPDATE TOURISM
SET
GUIDEBOOK = NULL;
WHERE CURRENT OF TC;
なお、上記のように BLOB データを削除したときでも、BLOB データがすぐに削除される
わけではありません。BLOB データは、実際には InterBase によりバージョンクリーンアッ
プが実行された時点で削除されます。また、次の文により実際の削除と同時に BLOB デー
タに占有されていたスペースが解放されます。
EXEC SQL
UPDATE TABLE SET Blob_COLUMN = NULL WHERE ROW = :myrow;
EXEC SQL
COMMIT;
/* すべてのアクティブなトランザクションの完了を待機します */
/* データベースのスイープを実行します */
ガベージコレクションを実行する際、削除対象のレコードに新しいバージョンのものがあ
れば、そのバージョンがなんらかの BLOB ID を参照しているかどうかの検査が行われま
す。この検査で BLOB ID を参照していなければ、レコード(BLOB)が削除されます。
BLOB ガベージコレクション処理は、次のように実行されます。レコードに BLOB ID が
格納されている場合は、使用されている BLOB ストレージの種類が調査されます。BLOB
が 1 つのページにある場合は、行インデックスインジケータが解放されます。1 つのページ
に BLOB だけがある場合は、そのページが解放されていることがページインジケータに
よって示されます。BLOB が一連のページにある場合は、BLOB インデックスが読み取ら
れ、すべてのページが解放されます。BLOB 自体を取得する必要はありません。
8-10 埋 め 込 み S Q L ガ イ ド
API 呼び出しによる BLOB データへのアクセス
API 呼び出しによる BLOB データへのアクセス
BLOB データに対しては、InterBase に用意されている API 呼び出しを使ってアクセスす
ることもできます。この種の API 呼び出しとしては次のようなものがあり、アクセスのほ
か、BLOB データの操作も行うことができます。
表 8.2
BLOB に関する API 呼び出し
関数
isc_blob_default_desc2()
説明
BLOB デスクリプタデータ構造体をデフォルト設定のままロードし
ます。
isc_blob_gen_bpb2()
ソース BLOB デスクリプタおよびターゲット BLOB デスクリプタ
をもとに BLOB パラメータバッファ(BPB)を生成します。BPB の
生成により、BLOB サブタイプとキャラクタセットへの動的アクセ
スが可能になります。
isc_blob_info()
開いている BLOB に関する情報を返します。
isc_blob_lookup_desc2()
BLOB のサブタイプ、キャラクタセット、セグメントサイズを検索
し、BLOB デスクリプタに格納します。
isc_blob_set_desc2()
BLOB デスクリプタの各パラメータの値を引数で指定した値に設定
します。
isc_cancel_blob()
BLOB を破棄するとともに内部領域を解放します。
isc_close_blob()
開いている BLOB を閉じます。
isc_create_blob2()
BLOB を格納するための場所を作成するとともに、BLOB を開き、
書き込みができる状態にします。また、オプションで BLOB データ
のサブタイプを変換するためのフィルタを指定できます。
isc_get_segment()
開いている BLOB からセグメントを読み取ります。
isc_open_blob2()
抽出とフィルタリング(オプション)を行うために、既存の BLOB
を開きます。
isc_put_segment()
BLOB セグメントを書き込みます。
isc_blob_default_desc2()、isc_blob_gen_bpb2()、isc_blob_lookup_desc2()、および
isc_blob_set_desc2() は、長さ METADATALENGTH の長いメタデータ名をサポートし
ます。isc_blob_default_desc() などの以前の呼び出しは、32 バイト以下のメタデータ名し
かサポートしません。
API 呼び出しの種類と概要は以上のとおりです。詳細は、
『API ガイド』を参照してくださ
い。
BLOB データのフィルタリング
BLOB フィルタを扱う場合、BLOB のサブタイプについて理解しておくことが必要です。
これは、BLOB フィルタが、BLOB データのサブタイプを別のサブタイプに変換するルー
チンだからです。InterBase には事前に、サブタイプ 0 をサブタイプ 1(TEXT)に変換す
第 8 章 BLOB データの操作
8-11
BLOB データのフィルタリング
るための BLOB フィルタ、およびサブタイプ 1(TEXT)をサブタイプ 0 に変換するため
の BLOB フ ィ ル タ の 2 つが用意されています。この 2 つの BLOB フィルタを標準
InterBase フィルタまたは内部フィルタと呼んでいます。このほか、BLOB フィルタは、
データの変換が必要な場合にユーザーが独自に作成することが可能で、この BLOB フィル
タを外部 BLOB フィルタと言います。たとえば、ビットマップイメージの形式を変換する
BLOB フィルタを作成することもできます。
標準 InterBase フィルタ
標準 InterBase フィルタを使用して、サブタイプ 0 の BLOB データ、つまり InterBase の
システムで使用される通常の BLOB データをサブタイプ 1(TEXT)の BLOB データに変
換することができます。また、その逆も可能です。
テキストフィルタ(データをテキストに変換する標準 InterBase フィルタ)では、サブタイ
プ 0 の BLOB データが列から読み取られる際、InterBase の標準のセグメント格納規則に
基づいて BLOB データの変換が行われます。つまり、BLOB データのセグメントにどのよ
うなデータが格納されていても、セグメントの末尾には必ず改行文字(¥n)が置かれてい
るものと見なされ、その改行文字に基づいて変換されます。
したがって、まずテキストフィルタでは、最初のセグメントに対応する文字列として、先
頭から最初の改行文字を含んだ範囲の文字列が返されます。この後、2 番めのセグメントに
対応する文字列として、2 番めのセグメントの先頭から 2 番めの改行文字を含んだ範囲の
文字列が返されます。以後、同じようにして文字列が変換されます。
ヒント
非テキストのサブタイプ(サブタイプ 0)を TEXT に変換するときは、FROM にサブタイ
プ 0 を TO にサブタイプ 1 を指定します。
外部 BLOB フィルタ
サブタイプ 0 とサブタイプ 1 の変換用の標準 InterBase フィルタは内蔵されていますが、
これに対して、外部 BLOB フィルタは独自に作成するとともにライブラリに含め、アプリ
ケーションにリンクする必要があります。
また、外部 BLOB フィルタは、記述、コンパイル、リンクのほか、処理する BLOB デー
タが格納されているデータベースに対して、宣言を行って初めて使用できるようになりま
す。
データベースに対する外部 BLOB フィルタの宣言
データベースに対して外部 BLOB フィルタを宣言する場合、DECLARE FILTER 文を使
用します。たとえば、次の文は SAMPLE という外部 BLOB フィルタの宣言例です。
EXEC SQL
DECLARE FILTER SAMPLE
INPUT_TYPE -1 OUTPUT_TYPE -2
ENTRY_POINT 'FilterFunction'
MODULE_NAME 'filter.dll';
8-12 埋 め 込 み S Q L ガ イ ド
BLOB データのフィルタリング
ここでは、フィルタの入力(INPUT_TYPE)はサブタイプが -1、出力(OUTPUT_TYPE)
はサブタイプが -2 と定義されています。また、この例では、INPUT_TYPE にはフィルタ
関数の定義で小文字のテキストが、OUTPUT_TYPE には大文字のテキストが指定されて
います。つまり、この SAMPLE は、BLOB データを小文字のテキストから大文字のテキ
ストに変換する機能を持った外部 BLOB フィルタということになります。
ENTRY_POINT と MODULE_NAME には、InterBase により呼び出される外部ルーチン
をパラメータとして指定します。この呼び出しにより、外部ルーチン(フィルタ)が動作
することになります。例では、MODULE_NAME パラメータは filter.dll となっており、こ
れはフィルタ SAMPLE の実行ファイルを格納したダイナミックリンクライブラリ(DLL)
を示します。また、ENTRY_POINT パラメータでは DLL へのエントリポイント(フィル
タ関数)を指定しています。なお、この例では、ダイナミックリンクライブラリに簡単な
ファイル名を使用しています。ただし、ユーザーがファイルをロードする場合を考えて、
ファイル名は完全修飾名を使って指定した方が無難です。
フィルタによる BLOB データの読み取りと書き込み
次の図は、SAMPLE フィルタによるデフォルトの処理を示したものです。このフィルタで
は、小文字のテキストが大文字のテキストに変換されます。
図 8.2
小文字のテキストから大文字のテキストへの変換
アプリケーション
abcdef
Filter
SAMPLE
BLOB
ABCDEF
また、SAMPLE フィルタは大文字のテキスト(サブタイプ -2)を読み取る際も使用でき
ます。この場合、大文字のテキストが小文字のテキスト(サブタイプ -1)に変換されます。
図 8.3
大文字のテキストから小文字のテキストへの変換
BLOB
ABCDEF
Filter
SAMPLE
アプリケーション
abcdef
アプリケーションでのフィルタの呼び出し
アプリケーションでフィルタを呼び出すには、BLOB カーソルの宣言で FILTER オプショ
ンを使ってフィルタを指定します。指定には FROM と TO を使用し、それぞれ、フィルタ
宣言での入力と出力のサブタイプを記述します。これで、その BLOB カーソルによる処理
が行われる場合、InterBase により自動的にフィルタが呼び出され変換が実行されます。
たとえば、次の文は、処理が INSERT の BLOB カーソル宣言の例です。ここでは、FILTER
オプションで SAMPLE フィルタを指定しており、したがって、BCINS1 カーソルによる
処理では必ず、この SAMPLE フィルタが使用されることになります。
EXEC SQL
DECLARE BCINS1 CURSOR FOR
第 8 章 BLOB データの操作
8-13
外部 BLOB フィルタの作成
INSERT Blob Blob1 INTO TABLE1
FILTER FROM -1 TO -2;
このカーソル宣言が InterBase により処理される際は、現在のデータベースに定義されてい
るフィルタの中に、FILTER オプションの FROM と TO で指定されている各サブタイプに
合致するフィルタがあるかどうかの検査が行われます。合致するフィルタがあれば、宣言し
たカーソル(BCINS1)による処理で、そのフィルタが呼び出されることになります。一
方、FROM と TO で指定されている各サブタイプに合致するフィルタがなければ、アプリ
ケーションに対してエラーが返されます。
外部 BLOB フィルタの作成
外部 BLOB フィルタの作成にあたっては、変換するデータの型を十分に把握しておかなけ
ればなりません。InterBase では、BLOB データに関してはデータ型の検査は厳格には行わ
れませんが、変換元のサブタイプと変換先のサブタイプとでは変換上の互換性が必要です。
この互換性を確保するのはプログラマの仕事となります。
フィルタの種類
フィルタは 2 つの種類に分けられます。つまり、必要に応じてセグメントごとにデータの
変換を行うものと、多数のセグメントを一括して変換するものです。
1 つのセグメントを読み取った直後に変換を行い、変換したデータをアプリケーションに渡
す処理を行うフィルタ。
BLOB 読み取りカーソルが開くと同時に、全部のセグメントを一括して読み取り、変換を
行った後、アプリケーションに対してセグメントごとにデータを送る処理を行うフィルタ。
したがって、アプリケーションでタイミングが問題となるときは、どちらの種類のフィル
タが適しているかよく検討して決める必要があります。
読み取り専用フィルタと書き込み専用フィルタ
フィルタは、使用目的や記述方法によっては、BLOB データに関して読み取り専用または
書き込み専用のどちらかとなることがあります。読み取り専用のフィルタを書き込み処理
で使用した場合、または逆の場合は、エラーが返されます。
フィルタ関数の定義
外部 BLOB フィルタを使用する場合、そのフィルタを DECLARE FILTER で宣言してお
かなければなりません。宣言ではエントリポイントを指定します。このエントリポイント
をフィルタ関数と呼びます。アプリケーションで BLOB 関連の処理を実行すると、この
フィルタ関数が呼び出されます。フィルタ関数は、InterBase とフィルタとの橋渡し役で、
InterBase とフィルタとのやりとりはすべてこのフィルタ関数を介して行われます。また、
フィルタ関数から別のフィルタ関数を呼び出し、そのフィルタを実行することもできます。
8-14 埋 め 込 み S Q L ガ イ ド
外部 BLOB フィルタの作成
図 8.4
アプリケーション、InterBase、フィルタの関係
アプリケーション
InterBase
フィルタ
フィルタ関数を定義するには、DECLARE FILTER 文の ENTRY_POINT パラメータにそ
の名前を記述し、MODULE_NAME パラメータにフィルタ関数が含まれているオブジェ
クトモジュールの名前を記述します。
フィルタ関数は、次の呼び出しシーケンスにしたがって宣言を行います。
filter_function_name(short action, isc_blob_ctl control);
上の文で、filter_function_name はフィルタ関数の名前で、ここに宣言するフィルタ関数の
名前を記述します。また、action は、全部で 8 つ定義されているアクションを示すパラメー
タ(isc_blob_filter_put_segment など)です。control は isc_blob_ctl、つまり BLOB 制御
構造体のインスタンスを表すパラメータです。isc_blob_ctl の定義は InterBase のヘッダー
ファイル ibase.h にあります。これらのパラメータについては、この章で後述します。
次のスケルトンフィルタコードは、jpeg_filter というフィルタ関数を宣言します。
#include <ibase.h>
#define SUCCESS 0
#define FAILURE 1
ISC_STATUS jpeg_filter(short action, isc_blob_ctl control)
{
ISC_STATUS status = SUCCESS;
switch (action)
{
case isc_blob_filter_open:
. . .
break;
case isc_blob_filter_get_segment:
. . .
break;
case isc_blob_filter_create:
. . .
break;
case isc_blob_filter_put_segment:
第 8 章 BLOB データの操作
8-15
外部 BLOB フィルタの作成
. . .
break;
case isc_blob_filter_close:
. . .
break;
case isc_blob_filter_alloc:
. . .
break;
case isc_blob_filter_free:
. . .
break;
case isc_blob_filter_seek:
. . .
break;
default:
status = isc_uns_ext /* サポートされていないアクション値 */
. . .
break;
}
return status;
}
上の例で、case 文の 8 つの記述(isc_blob_filter_open など)はアクションです。アプリ
ケーションの処理に応じて、このうちのいずれかがフィルタ関数宣言のパラメータである
action を介して、InterBase からフィルタ関数 jpeg_filter() に渡されます。また、フィルタ
関 数 宣 言 の パ ラ メ ー タ で あ る control を介して、BLOB 制御構造体のインスタンス
(isc_blob_ctl)が jpeg_filter に渡されます。
また、省略符号(…)はコードで、ここには、InterBase により渡されたアクション(イベ
ント)を受けて実行する処理を記述します。フィルタ関数宣言に渡されるアクションは、ア
プリケーションの処理の内容によって異なります。詳細は、8-18 ページの「フィルタ関数
の動作のプログラミング」を参照してください。
BLOB 制御構造体(isc_blob_ctl)は BLOB データの変換に使用される構造体で、InterBase
とフィルタの間で働きます。BLOB 制御構造体の詳細は、8-16 ページの「BLOB 制御構
造体の定義」を参照してください。
BLOB 制御構造体の定義
BLOB 制御構造体は、InterBase とフィルタとの間で機能し、この構造体を介して BLOB
データが変換されます。BLOB 制御構造体の宣言は、InterBase のインクルードファイル
ibase.h にあります。
BLOB 制御構造体は、次の 2 とおりの方法で使用されます。
1
アプリケーションでなんらかの BLOB 処理が行われた場合、フィルタ関数が呼び出さ
れ、同時に BLOB 制御構造体のインスタンスがそのフィルタ関数に渡されます。
2
内部フィルタ関数により、BLOB 制御構造体のインスタンスが InterBase の内部アクセ
スルーチンに渡されます。
8-16 埋 め 込 み S Q L ガ イ ド
外部 BLOB フィルタの作成
BLOB 制御構造体には各種の isc_blob_ctl フィールドが用意されており、上記のどの場合
でも、アクションに応じてなんらかの isc_blob_ctl フィールドが使用されることになりま
す。
たとえば、アプリケーションで BLOB INSERT(BLOB データの挿入)が試みられたとき
は、InterBase により isc_blob_filter_put_segment アクションと BLOB 制御構造体がフィ
ルタ関数に渡されます。フィルタ関数は、BLOB 制御構造体のインスタンスを InterBase に
渡します。この場合、InterBase に渡される BLOB 制御構造体の ctl_buffer フィールドに
は、書き込みに使用するセグメントデータ、つまり、アプリケーションの BLOB INSERT
文で指定された書き込みデータが格納されています。この場合、ctl_buffer フィールドには、
フィルタ関数に渡されるべきデータが格納されているので、このフィールドを IN フィール
ドと呼びます。また、フィルタ関数では、isc_blob_filter_put_segment アクションが指定さ
れている case 文で、データベースに書き込みを行う処理を記述しておかなければなりませ
ん。
一方、アプリケーションで FETCH(BLOB データの取り出し)が試みられたときは、フィ
ルタ関数の中の isc_blob_filter_get_segment アクションの case 文が処理を担当すること
になります。したがって、この場合、isc_blob_filter_get_segment アクションの case 文で、
データベースからセグメントデータを取り出して ctl_buffer フィールドに格納する処理を
記述しなければなりません。また、ctl_buffer フィールドは、この場合はフィルタ関数から
の出力に使用されるため、このフィールドを out フィールドと呼んでいます。
次の表は、BLOB 制御構造体(isc_blob_ctl)の各種フィールドと内容をまとめたもので
す。また、フィールドがフィルタ関数への入力に使用されるか(IN フィールド)、または
フィルタ関数からの出力に使用されるか(OUT フィールド)の別も示してあります。
表 8.3
isc_blob_ctl のフィールドの説明
フィールド名
説明
(*ctl_source)()
InterBase の内部 BLOB アクセスルーチンへのポインタ。(IN)
*ctl_source_handle
InterBase の内部 BLOB アクセスルーチンに渡す、isc_blob_ctl インスタン
スへのポインタ。(IN)
ctl_to_sub_type
変換先のサブタイプ。情報フィールド。複数の種類の変換を行う多目的フィ
ルタをサポートする。このフィールドと次の ctl_from_sub_type で、実行す
る変換を特定する。((IN)
ctl_from_sub_type
変換元のサブタイプ 情報フィールド。複数の種類の変換を行う多目的フィ
ルタをサポートする。このフィールドと上の ctl_from_sub_type で、実行す
る変換を特定する。((IN)
ctl_buffer_length
アクションが isc_blob_filter_put_segment の場合、ctl_buffer に格納された
セグメントデータの長さを格納。
(IN)
アクションが isc_blob_filter_get_segment の場合、ctl_buffer(内容は取り出
(IN)
された BLOB データ)によって示されるバッファのサイズを格納。
ctl_segment_length
現在のセグメントの長さ。アクションが isc_blob_filter_put_segment の場
合、このフィールドは使用されません。
アクションが isc_blob_filter_get_segment の場合、OUT フィールドで、取
り出されたセグメントの長さを格納(取り出されたセグメントが一部のと
きは、ctl_buffer_length で示されるバッファの長さは実際のセグメントの長
さより短くなる)
。
第 8 章 BLOB データの操作
8-17
外部 BLOB フィルタの作成
表 8.3
isc_blob_ctl のフィールドの説明 ( 続き )
フィールド名
説明
ctl_bpb_length
BLOB パラメータバッファの長さ。将来の改良のために予約。
*ctl_bpb
BLOB パラメータバッファへのポインタ。将来の改良のために予約。
*ctl_buffer
セグメントバッファへのポインタ。アクションが
isc_blob_filter_put_segment の場合、IN フィールドで、セグメントデータを
格納。
アクションが isc_blob_filter_get_segment の場合、OUT フィールドで、
フィルタ関数によりアプリケーションへ返されるセグメントデータを格納。
ctl_max_segment
BLOB の最長のセグメントの長さ。初期値は 0。フィルタ関数で値を設定。
情報フィールド。
ctl_number_segments
BLOB セグメントの総数。初期値は 0。フィルタ関数で値を設定。情報
フィールド。
ctl_total_length
BLOB の全体の長さ。初期値は 0。フィルタ関数で値を設定。情報フィール
ド。
*ctl_status
InterBase ステータスベクターへのポインタ。(OUT)
ctl_data[8]
8 つの要素からなるユーザー定義が可能な配列。このフィールドは、
isc_blob_filter_open ハンドラで作成されたメモリポインタやファイルハ
ンドルなどのリソースポインタを格納します。次にフィルタ関数を呼び出
したときは、そのリソースポインタが利用できるようになる。(IN/
OUT)
BLOB 制御構造体の情報フィールドの設定
BLOB 制御構造体(isc_blob_ctl)で、ctl_max_segment、ctl_number_segments、
ctl_total_length の 3 つは情報フィールドです。この 3 つのフィールドには、現在アクセス
している BLOB の情報が格納されます。
フィルタ関数では、この 3 つの情報フィールドの値をできるだけ正しく維持するようにし
ます。ただし、フィルタの機能によっては、情報フィールドの値は必ずしも正しく維持でき
な い こ と も あ り ま す。 た とえば、セグメント単位でデータを圧縮するフィルタで
は、ctl_max_segment の値は、全セグメントの処理が終了するまで判定できません。
なお、この 3 つの情報フィールドの値は参照用です。これらが InterBase の内部処理で使用
されることはありません。
フィルタ関数の動作のプログラミング
アプリケーションで、挿入や取り出しなど BLOB へのアクセス処理が行われた場合、その
処理に応じて、InterBase からフィルタ関数に対してアクションメッセージが渡されます。
このアクションメッセージは、フィルタ関数の宣言のパラメータである action を介して
フィルタ関数に渡されます。アクションは全部で 8 種類あり、どのアクションがフィルタ
関数に送られるかは、アプリケーションでの BLOB へのアクセス処理の種類によって決ま
ります。ibase.h ファイルには、次の 8 種類のアクションが定義されています。
#define isc_blob_filter_open
0
#define isc_blob_filter_get_segment 1
8-18 埋 め 込 み S Q L ガ イ ド
外部 BLOB フィルタの作成
#define
#define
#define
#define
#define
#define
isc_blob_filter_close
isc_blob_filter_create
isc_blob_filter_put_segment
isc_blob_filter_alloc
isc_blob_filter_free
isc_blob_filter_seek
2
3
4
5
6
7
次の表は、8 種類のアクションと、これらのアクションに対応する BLOB アクセス処理を
まとめたものです。また、各アクションによる処理も掲載してあります。
表 8.4
BLOB アクセス処理
動作
契機
BLOB アクセス処理
isc_blob_filter_open
アプリケーションによって
BLOB READ カーソルが開か
れた場合
ます。
BLOB 制御構造体の情報フィールドの値が設定され
メモリ割り当て、一時ファイルを開くなどの初期化
処理が実行されます。
必要な場合、ステータス変数が設定されます。このス
テータス変数の値は、フィルタ関数の戻り値となり
ます。
isc_blob_filter_get_segment
アプリケーションによって
BLOB FETCH 文が実行された
場合
BLOB 制御構造体の ctl_buffer と ctl_segment_length の各
フィールドの値が設定されます。ctl_buffer(が指すバッ
ファ)には変換されるセグメントが格納されます。
フィルタが BLOB をセグメント単位で変換する場
合、データの変換が実行されます。
ステータス変数が設定されます。このステータス変
数の値は、フィルタ関数の戻り値となります。
isc_blob_filter_close
アプリケーションによって
BLOB カーソルが閉じられた
場合
割り当てられているメモリの解放、一時ファイルを
閉じる、または削除するなどの終了処理が実行され
ます。
isc_blob_filter_create
アプリケーションによって
BLOB INSERT カーソルが開
かれた場合
ます。
BLOB 制御構造体の情報フィールドの値が設定され
メモリ割り当て、一時ファイルを開くなどの初期化
処理が実行されます。
必要な場合、ステータス変数が設定されます。このス
テータス変数の値は、フィルタ関数の戻り値となり
ます。
isc_blob_filter_put_segment
アプリケーションによって
BLOB INSERT 文が実行され
た場合
BLOB 制御構造体を介して渡されたセグメントデー
タが変換されます。
データベースにデータの書き込みが実行されます。
なお、変換処理でセグメントの長さが変わるときは、
その対応処理が必要です。
ステータス変数が設定されます。このステータス変
数の値は、フィルタ関数の戻り値となります。
第 8 章 BLOB データの操作
8-19
外部 BLOB フィルタの作成
表 8.4
BLOB アクセス処理 ( 続き )
動作
契機
BLOB アクセス処理
isc_blob_filter_alloc
InterBase によりフィルタリン
グの初期化が行われた場合。こ
のアクションは、アプリケー
ションの処理とは無関係です。
BLOB 制御構造体の情報フィールドの値が設定され
ます。
メモリ割り当て、一時ファイルを開くなどの初期化
処理が実行されます。
必要な場合、ステータス変数が設定されます。このス
テータス変数の値は、フィルタ関数の戻り値となり
ます。
isc_blob_filter_free
InterBase によるフィルタリン
グが終了した場合。このアク
ションは、アプリケーションの
処理とは無関係です。
isc_blob_filter_seek
内部でのフィルタ使用のため予
約。この動作は、外部 BLOB
フィルタには使用されません。
ヒント
割り当てられているメモリの解放、一時ファイルを
閉じる、または削除するなどの終了処理が実行され
ます。
isc_blob_filter_open によりメモリポインタやファイルハンドルが作成されますが、このよ
うなリソースポインタは BLOB 制御構造体(isc_blob_ctl)の ctl_data フィールドに格納
しておきます。これで、次回フィルタ関数が呼び出されたときは、このようなリソースポイ
ンタがそのまま利用できます。
フィルタ関数の戻り値
フィルタ関数は、そのフィルタ関数による処理のステータスを整数で返すように記述しな
ければなりません。この場合の戻り値としては、InterBase の内部ルーチンにより返される
InterBase の戻り値であればどのようなものでも返すことができます。
なお、フィルタの種類によっては、フィルタ関数で直接戻り値を返すように記述しなけれ
ばならないこともあります。次の表は、BLOB の処理に関する戻り値をまとめたものです。
表 8.5
BLOB フィルタの戻り値
マクロ定数
値
意味
SUCCESS
0
フィルタによる処理が無事終了したことを示します。また、BLOB
読み取り
(isc_blob_filter_get_segment)処理では、1 つのセグメン
ト全体の読み取りが完了したことを表します。
FAILURE
1
処理が失敗したことを示します。一般に、処理が失敗した場合、エ
ラーの内容を示す具体的な状態戻り値が返されます。
8-20 埋 め 込 み S Q L ガ イ ド
外部 BLOB フィルタの作成
表 8.5
BLOB フィルタの戻り値
マクロ定数
値
意味
isc_uns_ext
ibase.h を参照
受け取ったアクションがフィルタで対応してないものであったこ
フィルタが読み取り専用でアクションが
とを示します。たとえば、
isc_blob_filter_put_segment(セグメント挿入)だった場合、この
ケースに該当します。
isc_segment
ibase.h を参照
BLOB 読み取り処理で返され、用意されたバッファが小さく現在
のセグメントを全部格納できなかったことを示します。この場合、
格納されたセグメントの長さ(バイト)は ctl_buffer_length で示さ
れます。セグメントの残りの部分は、isc_blob_filter_get_segment
により読み取ることができます。
isc_segstr_eof
ibase.h を参照
BLOB 読み取り処理で返され、BLOB の末尾に達したことを示し
ます。つまり、読むべきセグメントがもう存在しないことを表しま
す。
InterBase の戻り値の詳細は、『言語リファレンス』を参照してください。
第 8 章 BLOB データの操作
8-21
8-22 埋 め 込 み S Q L ガ イ ド
第
第9章
章
配列の使い方
InterBase は、ほとんどのデータ型の配列をサポートしています。配列を使用すると、1 つ
の列に複数のデータを集合として格納できます。格納された配列は、1 つのまとまりとし
て、またはスライスと呼ばれる一連のデータ単位として扱うことができます。配列は、次の
ような場合に使用されます。
• 個々のデータがすべて同じデータ型である場合
• 複数のデータをグループ化でき、各グループを 1 つの列に格納して管理および操作する場
合。つまり、グループを構成するデータを個別の列に置くと適切でない場合
• 複数のデータをグループ化すると同時に、グループを構成するデータに対しても個別にア
クセスする場合
配列を構成するデータを配列要素と言います。配列には、BLOB を除き、InterBase でサ
ポートされているデータ型の要素を格納できます。しかし、配列型は配列要素として格納す
ることができません。ただし、InterBase では、多次元配列がサポートされています。配列
要素は、すべて同じデータ型でなければなりません。
配列の作成
配列は、CREATE DOMAIN または CREATE TABLE の各文を使って配列の列を指定し
て作成します。指定の方法は配列以外の列の場合と同じですが、配列では次元も指定しなけ
ればなりません。
配列インデックスの範囲は、–231 から +231–1 までです。
配列の列を指定する場合、次の文のようになります。ここでは、データ型が文字型、要素
が 4 つ、1 次元の配列の列を作成しています。
EXEC SQL
CREATE TABLE TABLE1
(
第 9 章 配列の使い方
9-1
配列の作成
NAME CHAR(10),
CHAR_ARR CHAR(10)[4]
);
上記のように、要素および次元は、データ型の右にブラケット([ ])で囲んで指定します。
CREATE TABLE と配列の指定の構文の詳細は、
『言語リファレンス』を参照してください。
多次元配列
InterBase では、16 次元までの多次元配列を扱うことができます。たとえば、2 次元、3 次
元、6 次元の 3 つの配列の列を定義する場合、記述は次のようになります。いずれも、デー
タ型は整数です。
EXEC SQL
CREATE TABLE TABLE1
(
INT_ARR2 INTEGER[4,5]
INT_ARR3 INTEGER[4,5,6]
INT_ARR6 INTEGER[4,5,6,7,8,9]
);
上の例では、INT_ARR2 は 4 行 5 列の配列、つまり全部で 20 の整数の要素を持つ配列に
なります。また、INT_ARR3 は全部で 120 の整数の要素、INT_ARR6 は全部で 60480 の
整数の要素を持った配列ということになります。
重要
InterBase は、行の大きな順に多次元配列を格納します。FORTRAN のような一部のホスト
言語は、配列は列の大きな順であると解釈しています。このような場合は、InterBase とホ
スト言語の間で要素の順序を正しく変換することに注意してください。
添字範囲の指定
InterBase では、配列次元は、添字という特定の上下境界の範囲を持っています。たいてい
の場合、添字の範囲は暗黙的です。配列の最初の要素は要素 1、2 番めは要素 2、最後は要
素 n です。たとえば、次の文は、4 つの整数の配列である列を持つテーブルを作成します。
EXEC SQL
CREATE TABLE TABLE1
(
INT_ARR INTEGER[4]
);
この配列の添字は 1, 2, 3, 4 です。
各配列の次元の上下限の別のセットは、配列の列を作成するときに明示的に定義されます。
たとえば、添字の下限が 0 で始まる配列に詳しい C プログラマは、おそらく同じように添
字の下限が 0 で始まる配列の列を作成するでしょう。
配列の添字を指定するには、最初の要素の添字(lower)と最後の要素の添字(upper)を
次の構文を使って記述します。
lower:upper
9-2 埋 め 込 み S Q L ガ イ ド
配列へのアクセス
たとえば、次の文では、4 つの要素で構成され、最初の要素の添字が 0、最後の要素の添字
が 3 という 1 次元の配列の列が作成されます。
EXEC SQL
CREATE TABLE TABLE1
(
INT_ARR INTEGER[0:3]
);
この配列の添字は 0, 1, 2, 3 です。
また、多次元の配列に明示的に添字を指定する場合は、それぞれの次元ごとに両端の添字
を記述した後、カンマで区切って次の次元の添字を記述します。たとえば、2 次元で要素が
どちらも 4 つ、各次元の配列要素に 0 から 3 までの添字が割り当てられた配列の列を作成
する場合は、次のように記述します。
EXEC SQL
CREATE TABLE TABLE1
(
INT_ARR INTEGER[0:3, 0:3]
);
配列へのアクセス
InterBase では、格納されている配列を 1 つの要素として扱うことができます。このほか、
配列の中の 1 つ、または複数の要素を個別に扱うこともできます。これらの要素を配列ス
ライスと呼んでいます。配列スライスには、1 つの要素で構成されるものと隣接する複数の
要素で構成されるものの 2 種類があります。
配列に対しては、次のような処理が可能です。
• 配列からのデータの取り出し
• 配列へのデータの挿入
• 配列スライスのデータの更新
• 配列スライスからのデータの取り出し
• 検索条件での配列要素の評価
ユーザー定義関数(UDF)では、配列の要素 1 つずつしか参照できません。
また、配列に対して次の処理を実行することはできません。
• DSQL での配列の次元に対する動的参照
• 配列スライスへのデータの挿入
• 各配列要素に NULL を設定
• 配列の列に対する MIN()、MAX()、SUM()、AVG()、COUNT() の各集計関数の使用
• SELECT の GROUP BY 句での配列の列の指定
• 配列スライスを作成元とするビューの作成
第 9 章 配列の使い方
9-3
配列へのアクセス
配列からのデータの取り出し
配列からは、次の手順でデータを取り出すことができます。
1
配列変数を宣言します。この場合、配列変数は、配列のデータを格納するのに十分な大
きさが必要です。たとえば、次の文は、配列変数を 3 つ作成する例です。
EXEC SQL
BEGIN DECLARE SECTION;
BASED ON TABLE1.CHAR_ARR char_arr;
BASED ON TABLE1.INT_ARR
int_arr;
BASED ON TABLE1.FLOAT_ARR float_arr;
EXEC SQL
END DECLARE SECTION;
2
カーソルを宣言し、データを選択する配列の列を指定します。たとえば、次のように記
述します。
EXEC SQL
DECLARE TC1 CURSOR FOR
SELECT NAME, CHAR_ARR[], INT_ARR[]
FROM TABLE1;
指定する配列名の右には必ずブラケット([ ])を付けなければなりません。このブラ
ケットがないと、配列のデータではなく、その列の配列 ID が読み取られます。
ブラケットを付けない場合に読み取られる配列 ID は、実際には BLOB ID です。ただ
し、この配列 ID は、InterBase の API 呼び出しを使って配列のデータにアクセスする
ときに利用できます。
3
カーソルを開いて、データを取り出します。
EXEC SQL
OPEN TC1;
EXEC SQL
FETCH TC1 INTO :name, :char_arr, :int_arr;
メモ
上の例ではカーソルを使って配列のデータを取り出していますが、必ずしもカーソルを使
用する必要はありません。たとえば、単一行 SELECT でデータを取り出してもかまいませ
ん。
上のように、配列のデータは行を基準に取り出されるので、取り出しの際は、この点に注
意しなければなりません。たとえば、2 行 3 列構成の 2 次元の配列では、行 1 の 3 つの要
素がまず取り出されます。この後、行 2 の 3 つの要素が取り出されることになります。
配列へのデータの挿入
配列にデータを挿入するには INSERT を使用します。この場合、挿入するデータは配列要
素すべてを埋めるものでなければなりません。それ以外の場合にはエラーが発生します。
配列へのデータの挿入は、次の手順で行います。
9-4 埋 め 込 み S Q L ガ イ ド
配列へのアクセス
1
配列のデータを格納する変数を宣言します。この場合、BASED ON 句を使用すると便
利で、ちょうど配列全体を埋める大きさの配列変数を用意できます。たとえば、次の文
は、BASED ON を使ってこのような配列変数を 3 つ作成する例です。
EXEC SQL
BEGIN DECLARE SECTION;
BASED ON TABLE1.CHAR_ARR char_arr;
BASED ON TABLE1.INT_ARR
int_arr;
BASED ON TABLE1.FLOAT_ARR float_arr;
EXEC SQL
END DECLARE SECTION;
2 変数にデータを入れます。
3 INSERT を使って配列に書き込みを行います。次に例を示します。
EXEC SQL
INSERT INTO TABLE1 (NAME, CHAR_ARR, INT_ARR, FLOAT_ARR)
VALUES ('Sample', :char_arr, :int_arr, :float_arr);
4
変更内容をコミットします。
EXEC SQL
COMMIT;
重要
配列へデータを挿入する場合、配列要素全部がデータで埋まるようにする必要があります。
そうでない場合、結果は保証されません。
配列スライスのデータの取り出し
SELECT 文を使用して、配列中の隣接する複数の要素を取り出すことができます。この配
列中の隣接する複数の要素を配列スライスと言います。この場合、SELECT で、配列名の
右にブラケット([ ])を使って配列スライスを指定します。ブラケットの中には、取り出す
要素を数値(添字)を使って指定します。配列が 1 次元で取り出すデータが 1 つのときは、
添字は 1 つになります。たとえば、1 次元配列の中の 2 番めの要素を取り出す場合、次の文
のようになります。
EXEC SQL
SELECT JOB_TITLE[2]
INTO :title
FROM EMPLOYEE
WHERE LAST_NAME = :lname;
また、1 次元配列の中の隣接する複数の要素を取り出すときは、その範囲の最初の要素
(lower_bound)と最後の要素(upper_bound)を添字で指定します。数値の間にはコロン
を挿入します。次のような構文になります。
[lower_bound:upper_bound]
次は、1 次元配列の中の隣接する 3 つの要素を取り出す場合の記述です。
EXEC SQL
SELECT JOB_TITLE[2:4]
INTO :title
第 9 章 配列の使い方
9-5
配列へのアクセス
FROM EMPLOYEE
WHERE LAST_NAME = :lname;
多次元配列では、それぞれの次元について取り出すデータの範囲を指定しなければなりま
せん。この場合、範囲は次元ごとにカンマで区切ります。構文は次のとおりです。
[lower:upper, lower:upper [, lower:upper ...]]
メモ
この構文で、外側の太字のブラケットは必ず入力しなければなりません。
次の文は、2 次元配列中、2 行(行 1、2)の 3 要素(列 1、2、3)を取り出す例です。
EXEC SQL
DECLARE TC2 CURSOR FOR
SELECT INT_ARR[1:2,1:3]
FROM TABLE1
InterBase では配列のデータは行を基準として扱われるため、ブラケットの最初の範囲が行
を表します。また、次の範囲が列を表し、各行について、ここで指定された列のデータが取
り出されるというように処理が行われます。
次に、配列スライスのデータを選択する場合の手順を説明します。
1
取り出すデータを格納するのに十分な大きさの変数を宣言します。次に例を示します。
EXEC SQL
BEGIN DECLARE SECTION;
char char_slice[11]; /* CHAR(10) データ型の 11 バイトの文字列 */
long int_slice[2][3];
EXEC SQL
END DECLARE SECTION;
上で、最初の char_slice 変数は、CHAR_ARR 列の 1 つの要素を格納するための変数
です。また、2 番めの int_slice 変数は、INT_ARR 整数列の配列スライス(6 要素)を
格納するためのものです。
2
カーソルを宣言するとともに、データを読み取る配列スライスを指定します。次はその
例です。
EXEC SQL
DECLARE TC2 CURSOR FOR
SELECT CHAR_ARR[1], INT_ARR[1:2,1:3]
FROM TABLE1
3
カーソルを開いて、データを取り出します。
EXEC SQL
OPEN TC2;
EXEC SQL
FETCH TC2 INTO :char_slice, :int_slice;
配列スライスのデータの更新
配列スライスのデータは、カーソルを使って更新できます。配列スライスのデータの更新
は、次の手順で行います。
9-6 埋 め 込 み S Q L ガ イ ド
配列へのアクセス
1
取り出すデータを格納するのに十分な大きさの変数を宣言します。たとえば、次のよう
になります。
EXEC SQL
BEGIN DECLARE SECTION;
char char_slice[11]; /* CHAR(10) データ型の 11 バイトの文字列 */
long int_slice[2][3];
EXEC SQL
END DECLARE SECTION;
上の例では、最初の char_slice 変数は、CHAR_ARR 配列(定義については配列の作
成のセクションを参照)の 1 つの要素を格納するための変数です。また、2 番めの
int_slice 変数は、INT_ARR 整数列の配列スライス(6 要素)を格納するためのもので
す。
2 SELECT を使用して、更新する配列スライスのデータを取り出します。たとえば、
CHAR_ARR 列(この列では取り出す要素は 1 つ)と INT_ARR 列から配列スライス
のデータを取り出す場合、記述は次のようになります。
EXEC SQL
DECLARE TC1 CURSOR FOR
SELECT CHAR_ARRAY[1], INT_ARRAY[1:2,1:3] FROM TABLE1;
EXEC SQL
OPEN TC1;
EXEC SQL
FETCH TC1 INTO :char_slice, :int_slice;
上の例では、CHAR_ARR 列と INT_ARR 列に格納されている配列スライスのデータ
がそれぞれ、char_slice と int_slice の 2 つの変数に格納されます。
3 2 つの変数に新規または修正済みのデータをロードします。
4 UPDATE 文を使用して、データを配列スライスに挿入します。char_slice と int_slice
の 2 つの変数に格納されているデータを CHAR_ARR 列と INT_ARR 列に挿入する場
合、UPDATE 文の記述は次のようになります。
EXEC SQL
UPDATE TABLE1
SET
CHAR_ARR[1] = :char_slice,
INT_ARR[1:2,1:3] = :int_slice
WHERE CURRENT OF TC1;
5
変更内容をコミットします。
EXEC SQL
COMMIT;
次の図は、この例によるデータ更新後の CHAR_ARR 列と INT_ARR 列の内容を示したも
のです。なお、添字は 0 で始まっています。また、値は参考値です。
第 9 章 配列の使い方
9-7
配列へのアクセス
char_arr values:
[0]:string0 [1]:NewString
int_arr values:
[0][0]:0 [0][1]:1
[1][0]:10 [1][1]:999
[2][0]:20 [2][1]:999
[3][0] 30 [3][1] 31
[2]:string2
[0][2]:2
[1][2]:999
[2][2]:999
[3][2] 32
[3]:string3
[0][3]:3
[1][3]:999
[2][3]:999
[3][3] 33
更新された値
検索条件での配列要素の評価
WHERE 句の検索条件を使用して、配列の中の要素を評価することができます。検索でき
るのは 1 つの配列要素に限られます。次に例を示します。
EXEC SQL
DECLARE TC2 CURSOR FOR
SELECT CHAR_ARR[1], INT_ARR[1:2,1:3]
FROM TABLE1
WHERE SMALLINT_ARR[1,1,1] = 111;
重要
検索条件で検索できるのは配列の中の 1 つの要素だけで、配列スライス(複数の要素の集
合)を検索することはできません。
変数による配列の添字の指定
配列の添字は、整数の変数を使って表すことができます。たとえば、次は、配列の添字とし
て getval と testval という 2 つの変数を使用しているカーソル宣言の例です。
EXEC SQL
DECLARE TC2 CURSOR FOR
SELECT CHAR_ARR[1], INT_ARR[:getval:1,1:3]
FROM TABLE1
WHERE FLOAT_ARR[:testval,1,1] = 111.0;
配列と算術式
WHERE 句の検索条件では、配列に算術式を組み込むことができます。たとえば、次は、配
列の記述に算術式を使った WHERE 句の例で、配列のデータのうち、この検索条件に適合
する行が 1 行ずつ取り出されます。
for (i = 1; i < 100 && SQLCODE == 0; i++)
{
EXEC SQL
SELECT ARR[:i] INTO :array_var
FROM TABLE1
WHERE ARR1[:j + 1] = 5;
process_array(array_var);
}
9-8 埋 め 込 み S Q L ガ イ ド
第
章
ストアドプロシージャ
第 10 章
ストアドプロシージャは、SQL 文を組み合わせて作成した独立型のプログラムコードで、
データベース中にメタデータとして格納されます。
アプリケーションで実行できるストアドプロシージャとの対話は以下のとおりです。
• ストアドプロシージャに対してパラメータを渡し、戻り値を受け取ることができます。
• ストアドプロシージャを直接呼び出してなんらかの処理を行うことができます。
• SELECT 文でテーブルやビューのかわりにストアドプロシージャを指定できます。
ストアドプロシージャには、次のような利点があります。
• 異なるアプリケーションでコードを共有できます。つまり、よく使用する処理をストアド
プロシージャとして作成してデータベースに格納しておくと、そのデータベースに対して
アクセスを行うアプリケーションであれば、どのアプリケーションからでもストアドプロ
シージャの利用が可能になります。ストアドプロシージャは、InterBase の対話型 SQL ツー
ルである isql でも使用できます。
• 効率のよいモジュール設計が可能です。ストアドプロシージャは複数のアプリケーション
で使用できるため、コードをアプリケーションごとに記述する必要がなくなります。また、
アプリケーションのサイズも縮小できます。
• 保守の簡略化が図れます。ストアドプロシージャの内容を変更した場合、変更は、そのス
トアドプロシージャを使用しているアプリケーションすべてに対して自動的に有効になり
ます。このため、アプリケーションの再コンパイル、再リンクが不要となります。
• 動作性能、特にリモートクライアントによるアクセスの動作パフォーマンスが向上します。
これは、ストアドプロシージャがクライアントではなくサーバーによって実行されるため
です。
この章では、アプリケーションでのストアドプロシージャの呼び出しと実行について説明
します。ストアドプロシージャの作成方法の詳細は、
『データ定義ガイド』を参照してくだ
さい。
第 10 章 ストアドプロシージャ
10-1
ストアドプロシージャの使い方
ストアドプロシージャの使い方
ストアドプロシージャの呼び出し方には 2 種類あり、その呼び出し方に応じて、便宜上ス
トアドプロシージャを次の 2 種類に分けています。
• アプリケーションの SELECT 文で、テーブルやビューのかわりに指定したストアドプロ
シージャを選択プロシージャ(select procedure)と言います。選択プロシージャでは、必ず
1 つ、または複数の値が返されるように出力パラメータを指定しなければなりません。こ
の指定がない場合、エラーとなります。
• アプリケーションで EXECUTE PROCEDURE 文を使ってストアドプロシージャを直接
呼び出す場合、そのストアドプロシージャを実行可能プロシージャ(excutable procedure)
と呼びます。実行可能プロシージャでは、必ずしも値が返される必要はありません。
上記の 2 種類のストアドプロシージャはいずれも CREATE PROCEDURE を使って定義
し、どちらの場合も構文は同じです。ただし、コードでの記述方法と用途は異なります。つ
まり、選択プロシージャでは必ず 1 行、または複数行(該当行が見つからないときにはな
し)が取り出されます。したがって、プログラムから見ると、選択プロシージャはテーブ
ルまたはビューと同等ということになります。一方、実行可能プロシージャはルーチンその
もので、プログラムから呼び出され、通常なんらかの値を返します。
ストアドプロシージャは、内容によって、同一のものを選択プロシージャと実行可能プロ
シージャの両方に使用することもできますが、あまりお勧めできません。ストアドプロシー
ジャは、通常 SELECT 文で使用するプロシージャ(選択プロシージャ)と、EXECUTE
PROCEDURE 文で使用するプロシージャ(実行可能プロシージャ)に分けて記述するの
が原則です。ストアドプロシージャの作成方法の詳細は、
『データ定義ガイド』を参照して
ください。
ストアドプロシージャとトランザクション
ストアドプロシージャは、その記述部分で起動しているトランザクションとともに動作し
ます。したがって、ともに機能しているトランザクションの動作が ROLLBACK によって
ロールバックされた場合、そのストアドプロシージャの動作もロールバックされます。ま
た、ストアドプロシージャの動作は、ともに機能しているトランザクションの処理がコミッ
トされて初めて有効になります。
ストアドプロシージャとセキュリティ
アプリケーションでストアドプロシージャを呼び出す場合、アプリケーションのユーザー
に対してストアドプロシージャの EXECUTE 特権が与えられていなければなりません。
EXECUTE 特権の付与には GRANT 文を使用します。また、付与した EXECUTE 特権を
取り消す場合は REVOKE 文を使用します。特権の付与の詳細は、『データ定義ガイド』を
参照してください。
10-2 埋 め 込 み S Q L ガ イ ド
選択プロシージャの使い方
また、ストアドプロシージャでデータベースのオブジェクトにアクセスする場合、必ずし
たがわなければならないことがあります。アプリケーションのユーザーまたは呼び出した
ストアドプロシージャのどちらかに、アクセスするオブジェクトに対する特権が付与され
ていなければなりません。ストアドプロシージャに特権を付与するには GRANT 文を使用
します。また、特権の取り消しには REVOKE 文を使用します。
選択プロシージャの使い方
選択プロシージャは、SELECT 文でテーブルやビューのかわりに指定して使用します。こ
れで、選択プロシージャ内の定義にしたがって該当行が取り出されます(該当行がない場
合、行は取り出されません)。また、選択プロシージャでは、必ず出力パラメータを指定し
なければなりません。この出力パラメータがない場合はエラーとなります。戻り値が特定さ
れないときは、デフォルトで NULL が返されます。
テーブルやビューに比べて、選択プロシージャには次の利点があります。
• 入力パラメータを指定できるとともに、その出力を利用できます。
• 制御文、ローカル変数、データ操作文を使用できるので、アプリケーションの柔軟性が大
幅に向上します。
CREATE PROCEDURE を使って選択プロシージャを定義する場合、まず、選択プロシー
ジャの名前を記述し、次に入力パラメータをかっこで囲んで指定します。入力パラメータ
が複数のときは、各引数をカンマで区切ります。
たとえば、次の文は、GET_EMP_PROJ という名前の選択プロシージャを定義している
isql スクリプトファイルの例です。この GET_EMP_PROJ は、入力パラメータとして社員
番号(EMP_NO)が渡されると、その社員が担当しているプロジェクト番号(EMP_PROJ)
を返すという選択プロシージャです。
CREATE PROCEDURE GET_EMP_PROJ (emp_no SMALLINT)
RETURNS (emp_proj SMALLINT) AS
BEGIN
FOR SELECT PROJ_ID
FROM EMPLOYEE_PROJECT
WHERE EMP_NO = :emp_no
INTO :emp_proj
DO
SUSPEND;
END ;
次の文は、この GET_EMP_PROJ の SELECT 文での使用例で、ここでは、取り出された
プロジェクト番号(PROJ_ID)は変数の number に渡されます。
SELECT PROJ_ID FROM GET_EMP_PROJ (:number);
第 10 章 ストアドプロシージャ
10-3
実行可能プロシージャの使い方
選択プロシージャの呼び出し
アプリケーションで選択プロシージャを使用する場合、通常のテーブル名やビュー名のか
わりに、その選択プロシージャの名前を記述します。テーブルやビューの指定が可能であ
れば、どの箇所でも選択プロシージャを指定できます。また、選択プロシージャの右には入
力パラメータをかっこで囲んで記述します。パラメータが複数のときは、各引数をカンマ
で区切ります。
EXEC SQL
SELECT PROJ_ID FROM GET_EMP_PROJ (:emp_no)
ORDER BY PROJ_ID;
重要
InterBase では、選択プロシージャを呼び出してビューの作成を行うことはできません。
カーソル宣言での選択プロシージャの使用
選択プロシージャは、カーソル宣言の中でも使用できます。たとえば、次は、PROJECTS
という名前のカーソル宣言で、テーブルのかわりに GET_EMP_PROJ 選択プロシージャを
指定している例です。
EXEC SQL
DECLARE PROJECTS CURSOR FOR
SELECT PROJ_ID FROM GET_EMP_PROJ (:emp_no)
ORDER BY PROJ_ID;
また、次の例は、この PROJECTS カーソルを埋め込み SQL で記述している C のコードで
す。このコードでは、プロジェクト番号が標準出力に出力されます。
EXEC SQL
OPEN PROJECTS
/* 従業員プロジェクトを出力します */
while (SQLCODE == 0)
{
EXEC SQL
FETCH PROJECTS INTO :proj_id :nullind;
if (SQLCODE == 100)
break;
if (nullind == 0)
printf("¥t%s¥n", proj_id);
}
実行可能プロシージャの使い方
実行可能プロシージャは、アプリケーションで直接呼び出して使用します。この実行可能
プロシージャは、複数のアプリケーションで同一のデータベースに対して同じ処理を行う
場合に便利です。実行可能プロシージャでは、実行時に入力パラメータを指定することがで
10-4 埋 め 込 み S Q L ガ イ ド
実行可能プロシージャの使い方
きます。この場合、このパラメータが呼び出し元のプログラムから渡されます。また、オ
プションで出力パラメータを指定することもでき、この場合、呼び出し元のプログラムに
1 つの行が返されます。
入力パラメータは、実行可能プロシージャの名前の右に記述します。入力パラメータが複
数の場合、各引数をカンマで区切ります。
メモ
実行可能プロシージャでは、複数行を返すことはできません。
実行可能プロシージャの実行
アプリケーションで実行可能プロシージャを実行する場合、次の構文で実行可能プロシー
ジャを記述します。
EXEC SQL
EXECUTE PROCEDURE name [:param [[INDICATOR]:indicator]]
[, :param [[INDICATOR]:indicator] ...]
[RETURNING_VALUES :param [[INDICATOR]:indicator]
[, :param [[INDICATOR]:indicator]...]];
実行可能プロシージャで入力パラメータを指定する場合、入力パラメータはリテラル値(7
や “Fred” など)または変数のどちらでもかまいません。一方、出力パラメータを指定する
場合、RETURNING_VALUES 句で変数を記述しなければなりません。戻り値は、この変
数に格納されることになります。
たとえば、DEPT_BUDGET という実行可能プロシージャをリテラルの入力パラメータを
使って実行する場合、記述は次のようになります。
EXEC SQL
EXECUTE PROCEDURE DEPT_BUDGET 100 RETURNING_VALUES :sumb;
次の文も同じプロシージャを呼び出していますが、入力パラメータにリテラル値ではなく
ホスト変数を使用しています。
EXEC SQL
EXECUTE PROCEDURE DEPT_BUDGET :rdno RETURNING_VALUES :sumb;
インジケータ変数
実行可能プロシージャの入力パラメータと出力パラメータには、どちらもインジケータ変
数を添えることができます。このインジケータ変数により、NULL の検査が可能になりま
す。特に、出力パラメータの値が NULL(不定)であるかどうかを検査する場合、必ずイ
ンジケータ変数を使用します。インジケータ変数は INDICATOR の右に記述しますが、こ
の INDICATOR は省略することもできます。インジケータ変数がマイナスのときは、パラ
メータの値が NULL(不定)であることを示します。また、0 のときはパラメータの値が
NULL 以外であることを表します。インジケータ変数の詳細は、第 6 章「データ処理」を
参照してください。
DSQL アプリケーションでのストアドプロシージャの実行
ストアドプロシージャは DSQL アプリケーションで実行することもできます。実行は、次
の手順で行います。
第 10 章 ストアドプロシージャ
10-5
実行可能プロシージャの使い方
1 PREPARE 文を使ってストアドプロシージャの分析と実行の準備を行います。
PREPARE の構文は次のとおりです。
EXEC SQL
PREPARE sql_statement_name FROM :var | '<statement>';
2 DESCRIBE INPUT を使用して、入力用の XSQLDA を用意します。構文は次のとお
りです。
EXEC SQL
DESCRIBE INPUT sql_statement_name INTO SQL DESCRIPTOR
input_xsqlda;
3 DESCRIBE OUTPUT を使用して、出力用の XSQLDA を用意します。構文は次のと
おりです。
EXEC SQL
DESCRIBE OUTPUT sql_statement_name INTO SQL DESCRIPTOR
output_xsqlda;
なお、出力用の XSQLDA は、ストアドプロシージャによって値が返される場合に限
り必要になります。
4
次のようにして、文を実行します。
EXEC SQL
EXECUTE statement USING SQL DESCRIPTOR input_xsqlda
INTO DESCRIPTOR output_xsqlda;
ストアドプロシージャの入力パラメータを疑問符(?)にして記述しておくと、実行時の値
を渡すことができるようになります。次は、ADD_EMP_PROJ というストアドプロシー
ジャのパラメータを疑問符で記述し、DSQL で用意と実行を行う例です。
. . .
strcpy(uquery, "EXECUTE PROCEDURE ADD_EMP_PROJ ?, ?");
. . .
EXEC SQL
PREPARE QUERY FROM :uquery;
EXEC SQL
DESCRIBE INPUT QUERY INTO SQL DESCRIPTOR input_xsqlda;
EXEC SQL
DESCRIBE OUTPUT QUERY INTO SQL DESCRIPTOR output_xsqlda;
EXEC SQL
EXECUTE QUERY USING SQL DESCRIPTOR input_xsqlda INTO SQL DESCRIPTOR
output_xsqlda;
. . .
10-6 埋 め 込 み S Q L ガ イ ド
第
章
イベントの操作
第 11 章
この章では、InterBase のイベントメカニズムについて説明します。また、アプリケーショ
ン上で、イベントとの関係を登録してイベントに応答する方法について説明します。
InterBase のイベントメカニズムを利用することで、いずれかのアプリケーションでなんら
かの処理やデータベースの変更が行われた場合、その処理や変更を動作中の別のアプリ
ケーションにも同時に伝えることができます。この場合、アプリケーション同士の直接の
通信は必要ありません。また、イベントが発生したかどうかを検査する定期ポーリングも
行われないので、そのための CPU 時間も節約できます。
InterBase のイベントメカニズム
InterBase では、イベントとは、トリガーやストアドプロシージャによって InterBase のイ
ベントマネージャに渡されるメッセージを言います。メッセージ(イベント)の内容は特
定の状態や動作で、INSERT、UPDATE、DELETE などによるデータベース上での変更
(挿入、更新、削除)が代表的なものとして挙げられます。イベントは、そのイベントが発
生したときに機能していたトランザクションがコミットされた時点で、トリガーやストア
ドプロシージャによって InterBase のイベントマネージャに実際に渡されます。
トリガーやストアドプロシージャから送られてきたイベントは、InterBase のイベントマ
ネージャ側で保存、管理されます。このほか、イベントとの関係を登録しているアプリケー
ションについても、イベントマネージャ側で管理が行われます。新たなイベントがイベント
マネージャに送られるたびに、イベントマネージャから、関係するアプリケーションに対
してイベントが発生したことの通知が送られます。
このように、イベントはイベントマネージャによって管理されますが、イベントに対して
アプリケーション側で応答する場合、そのアプリケーションでは次のような準備をしてお
く必要があります。
1
2
イベントマネージャに対して、イベントとの関係を登録しておきます。
イベントマネージャからのイベントの通知を待ちます。
第 11 章 イベントの操作
11-1
イベントの発生
3
必要なイベントを識別します(アプリケーションで複数のイベントを待つ場合)。
以上、InterBase のイベントメカニズムの概要を説明しました。イベントメカニズムは次の
3 つの要素で構成されます。
• トリガーまたはストアドプロシージャ:イベントは、このトリガーやストアドプロシージャ
によってイベントマネージャに送られます。
• イベントマネージャ:このイベントマネージャによってイベントキュー(イベントの待ち
行列)が管理されます。また、イベントが発生するたびに、それがアプリケーションに通
知されます。
• アプリケーション:イベントマネージャにイベントとの関係を登録し、そのイベントが発
生するのを待っているアプリケーションです。
以上のような 3 つの要素が揃っている場合、いずれかのアプリケーションでイベントを発
生させるようなストアドプロシージャ(またはトリガー)が実行されると、そのイベント
がイベントマネージャに送られます。同時に、イベントを待つアプリケーションに対して
通知が送られ、通知を受けたアプリケーション側での実際の処理が行われます。
イベントの発生
イベントメカニズムを使用する場合、イベントを発生させるトリガーまたはストアドプロ
シージャをアプリケーションに用意しなければなりません。イベントの種類としては、
INSERT、UPDATE、DELETE などによるデータベースの変更(挿入、更新、削除)が代
表的です。このようなイベントは、それぞれ、トリガーまたはストアドプロシージャに
POST_EVENT 文を記述しておくことで発生させることができます。
POST_EVENT を記述しておいた場合、発生したイベントはトランザクションのコミット
が完了した時点でイベントマネージャに送られます。この後、イベントマネージャによって
のそのイベントに関する通知がイベントを登録しているアプリケーションに送られます。
このようなイベント発生の機能を持ったトリガーやストアドプロシージャをイベントア
ラータと呼ぶこともあります。たとえば、次はトリガー作成のための isql スクリプトで、こ
こで作成されたトリガーは、アプリケーション上でテーブルへのデータ挿入が行われるた
びにイベント(INSERT のイベント)をイベントマネージャに送る働きをします。
CREATE TRIGGER POST_NEW_ORDER FOR SALES
ACTIVE
AFTER INSERT
POSITION 0
AS
BEGIN
POST_EVENT 'new_order';
END ;
イベント名の長さには制限はありません。
メモ
POST_EVENT はストアドプロシージャまたはトリガー専用で、ストアドプロシージャと
トリガーの中だけで使用できます。
11-2 埋 め 込 み S Q L ガ イ ド
イベントとの関係の登録
トリガーとストアドプロシージャでの POST_EVENT の記述方法の詳細は、『データ定義
ガイド』を参照してください。
イベントとの関係の登録
アプリケーション側でイベントマネージャからのイベント通知を受け取る場合、受け取り
たいイベントについて、イベントマネージャに事前に登録しておかなければなりません。こ
れで、発生したイベントの通知を待機できるようになります。イベントとの関係の登録には
EVENT INIT 文を使用します。EVENT INIT には次の 2 つの引数が必要です。
• アプリケーション独自のリクエストハンドル:このハンドルがイベントマネージャに送ら
れます。
• 通知を要求するイベント名:かっこで囲んで指定します。
最初のアプリケーション独自のリクエストハンドルは、同じものを後続の EVENT WAIT
文でも指定します。これで、イベントマネージャからの通知を受け取る準備ができます。こ
のリクエストハンドルは、イベントマネージャで、特定のイベント通知をどのアプリケー
ションに送るかを判定するために使用されます。判定後、通知がアプリケーションに送ら
れ、その通知にアプリケーションが応答することになります。
2 番めの引数であるイベント名はかっこで括って指定します。この名前は、トリガーまたは
ストアドプロシージャからイベントマネージャに送られるイベント名と同じでなければな
りません。この名前が異なる場合、通知は行われません。
1 つのイベントとの関係を登録する場合、EVENT INIT の構文は次のようになります。
EXEC SQL
EVENT INIT request_name (event_name);
ここで、request_name はリクエストハンドルです。event_name はイベント名で、長さは
15 字以内でなければなりません。この event_name は、引用符で囲んだ定数でも変数でも
かまいません。
たとえば、アプリケーションで次の EVENT INIT 文を記述すると、
RESPOND_NEW というリクエストハンドルが作成され、“new_order” というイベントと
の関係が登録されます。
EXEC SQL
EVENT INIT RESPOND_NEW ('new_order');
また、次の記述では、リクエストハンドルは RESPOND_NEW で同じですが、myevent と
いう変数で示されるイベントとの関係が登録されます。
EXEC SQL
EVENT INIT RESPOND_NEW (:myevent);
以上のようにしてイベントとの関係を登録しても、このままではイベントの通知を受け取
ることはできません。このほか、EVENT WAIT を使ってイベントの通知を待つ必要があ
ります。イベントの通知の待機の詳細は、11-4 ページの「EVENT WAIT によるイベント
の通知の待機」を参照してください。
第 11 章 イベントの操作
11-3
複数のイベントとの関係の登録
メモ
イベントとの関係の登録は、EVENT INIT の他に、InterBase の API 呼び出しを使って行
うこともできます。また、イベントの通知の受け取りについては、非同期トラップ(AST)
関数を使って実現することもできます。この API 呼び出しと非同期トラップ(AST)関数
の組み合わせでは、イベントの通知を待っている間もアプリケーションで他の処理が可能
になります。API によるイベントの処理の詳細は、『API ガイド』を参照してください。
複数のイベントとの関係の登録
場合によっては、1 つのリクエストハンドルで複数のイベント発生を待ちたいこともありま
す。EVENT INIT では、2 番めの引数に複数のイベント名を指定することができるので、
同時に複数のイベント通知を待つことができます。この場合の EVENT INIT の構文は次
のようになります。
EXEC SQL
EVENT INIT request_name (event_name [event_name ...]);
event_name はイベント名で、長さは 15 字以内でなければなりません。また、event_name
はトリガーまたはストアドプロシージャからイベントマネージャに送られるイベント名と
同じでなければなりません。この名前が異なる場合、通知は行われません。たとえば、ア
プリケーションで次の EVENT INIT 文を記述すると、RESPOND_MANY というリクエ
ストハンドルが作成され、“new_order”、“change_order”、“cancel_order” という 3 つの
イベントとの関係が登録されます。
EXEC SQL
EVENT INIT RESPOND_MANY ('new_order', 'change_order',
'cancel_order');
メモ
上では、一意なリクエストハンドルを使って複数のイベントとの関係を登録しています。こ
のほか、EVENT INIT 文を個別に使用して、それぞれ 1 つのリクエストハンドルと 1 つ
(または複数の)のイベントを指定することもできます。ただし、イベント通知の受け取り
については、リクエストハンドルごとに個別に待機しなければなりません。
EVENT WAIT によるイベントの通知の待機
イベントとの関係を登録しても、そのままではイベントの通知を受け取ることはできませ
ん。通知を待つには、EVENT WAIT 文が必要です。この EVENT WAIT 文により、通知
の受け取りの準備ができていることがイベントマネージャに伝えられます。また、通知を
受け取るまでの間、アプリケーションの処理が一時中断されることになります。
受け取り準備の連絡とアプリケーションの処理の一時中断を行いたい場合、EVENT WAIT
の構文は次のようになります。
EXEC SQL
EVENT WAIT request_name;
request_name は、先行の EVENT INIT 文で指定したリクエストハンドルの名前です。
次は、EVENT INIT 文と EVENT WAIT 文を組み合わせた記述例で、この 2 つの文によ
りリクエストハンドルの作成とイベント通知の受け取りが行われます。
11-4 埋 め 込 み S Q L ガ イ ド
イベントに対する応答
EXEC SQL
EVENT INIT RESPOND_NEW ('new_order');
EXEC SQL
EVENT WAIT RESPOND_NEW;
前述のように、EVENT WAIT が実行されると、アプリケーションの処理はイベントの通
知を受け取るまで一時的に中断されます。
メモ
アプリケーションには複数の EVENT WAIT 文を記述できます。EVENT WAIT 文を複数
記述した場合、最初の EVENT WAIT 文でアプリケーションの処理は中断します。処理が
再開するたびに、次の EVENT WAIT 文でも同様にアプリケーションの処理が中断します。
アプリケーションでイベントの処理が行われている最中に別のイベントが発生したとき
は、そのイベントはイベントマネージャでいったん保持され、アプリケーションが再度待
機状態に入ったときに通知が送られます。
イベントに対する応答
イベントマネージャからの通知を受け取ると、それまで一時中断していたアプリケーション
の処理が再開します。この後、EVENT WAIT 文の後続の処理が実行されることになります。
アプリケーションで 1 つのリクエストハンドルを使って複数のイベントとの関係を登録し
ている場合、どのイベントをアプリケーション側で実際に処理するのか判定する必要があ
ります。この判定には、イベント配列である isc_event[] を使用します。この isc_event[] は、
前処理時に自動的に作成されます。各配列要素は、EVENT INIT の第 2 引数で指定したイ
ベント名に対応しています。また、要素の値は、イベントと同じリクエストハンドルが指定
されている EVENT WAIT 文が実行された後のイベントの発生回数に一致します。
たとえば、次のコードでは、3 つのイベントとの関係が登録され、いずれかのイベント通知
に対する待機状態に入ります。
EXEC SQL
EVENT INIT RESPOND_MANY ('new_order', 'change_order',
'cancel_order');
EXEC SQL
EVENT WAIT RESPOND_MANY;
この後、“new_order”、“change_order”、“cancel_order” のいずれかのイベントが発生し、処
理に関係しているトランザクションがコミットされると、イベントマネージャによりイベン
トの通知がアプリケーションに送られます。これで、アプリケーションの処理が再開します。
次のコードは、アプリケーションで発生したイベントを特定する方法を示します。
for (i = 0; i < 3; i++)
{
if (isc_$event[i] > 0)
{
/* このイベントが発生したため処理します */
. . .
}
}
第 11 章 イベントの操作
11-5
11-6 埋 め 込 み S Q L ガ イ ド
第
章
エラー処理と回復
第 12 章
SQL アプリケーションでは、実行時エラーのトラップと応答、および実行時エラーからの
復旧に関する処理を用意しておかなければなりません。実行時エラーとは、ユーザーがア
プリケーションを使用したときに発生する可能性があるエラーを言います。この章では、
SQL での標準のエラー処理について説明します。この方法は標準なので移植性もありま
す。このほか、InterBase 独自のエラー処理についても紹介してあります。
標準のエラー処理
SQL では、SQL 文が実行されるたびに、なんらかのステータスインジケータ(値)が
SQLCODE 変数に返されます。この SQL 変数は、gpre による SQL プログラムの前処理
時に自動的に宣言されます。SQLCODE 変数に格納される値(SQLCODE の値)としては、
次の表のようなものがあります。
表 12.1 SQLCODE の値
値
意味
0
成功
1–99
警告または情報メッセージ
100
EOF(ファイルの終わり)。処理すべきデータなし
<0
エラー。ステートメントの実行失敗
SQL 処理の後には、SQLCODE の値を必ず検証して、実行時エラーをトラップするととも
に必要な処理を行う必要があります。SQLCODE を検証する場合、次の 3 つの方法があり
ます。検証後、エラー処理を行います。
• WHENEVER 文を使用する方法。この文を使用することで、SQLCODE の検査とエラー
発生時の処理を自動化することができます。
第 12 章 エラー処理と回復
12-1
標準のエラー処理
• SQLCODE を直接検査する方法。この検査は、各 SQL 文のすぐ後で行います。
• WHENEVER 文と直接検査を組み合わせて対処する方法
上の 3 種類の方法は、いずれも短所と長所があります。これらについては、この章におい
て以降の 3 つのトピックで説明します。
WHENEVER 文
WHENEVER 文を使用することで、最小限のコーディングで SQL のエラーを処理するこ
とができます。WHENEVER 文では、エラー、警告、EOF(ファイルの終わり)の 3 種類
のエラー処理コードを指定できます。エラー処理コードが SQLCODE に格納されるとなん
らかの処理を実行するように、プログラムを記述できます。WHENEVER の構文は次のと
おりです。
EXEC SQL
WHENEVER {SQLERROR | SQLWARNING | NOT FOUND}
{GOTO label | CONTINUE};
WHENEVER を記述しておいた場合、なんらかのエラー(処理失敗、警告、EOF)が発生
したときには後続の SQL 文の処理がすべて省略され、label で指定されたコードに処理が
移行します。
このように WHENEVER 文では後続の文の処理が省略されるため、WHENEVER 文は通
常、プログラムの先頭近くに記述するようにします。たとえば、次は、C コードの main()
関数の先頭に WHENEVER を記述し、SQL エラーをトラップしている例です。
main()
{
EXEC SQL
WHENEVER SQLERROR GOTO ErrorExit;
. . .
Error Exit:
if (SQLCODE)
{
print_error();
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT;
exit(1);
}
}
. . .
print_error()
{
printf("Database error, SQLCODE = %d¥n", SQLCODE);
}
12-2 埋 め 込 み S Q L ガ イ ド
標準のエラー処理
WHENEVER 文には記述によって次の 3 種類があります。それぞれ次のような場合に実行
されます。
• WHENEVER に SQLERROR を指定した場合:SQLCODE がマイナスの値のとき、つま
り文の処理が失敗したときに、WHENEVER 文が実行されます。
• WHENEVER に SQLWARNING を指定した場合:SQLCODE の値が 1 から 99 までのい
ずれかの場合、つまり実行されたが、文になんらかの問題が発生した場合、WHENEVER
文が実行されます。
• WHENEVER に NOT FOUND を指定した場合:SQLCODE の値が 100 のとき、つまり
FETCH や SELECT の処理で EOF に達したときに、WHENEVER 文が実行されます。
SQLERROR、SQLWARNING、NOT FOUND の機能はそれぞれ独立しており、指定し
なかったときは、該当するエラーが発生してもトラップは行われません。たとえば、
WHENEVER NOT FOUND の記述がない場合、FETCH や SELECT により EOF に達し
たとき(SQLCODE が 100)でもエラーはトラップされず、プログラムは通常どおり継続
されます。
なお、WHENEVER 文に CONTINUE を記述することで、エラーを無視することもできま
す。たとえば、次のようになります。
. . .
EXEC SQL
WHENEVER SQLWARNING
CONTINUE;
. . .
上の場合、文は実行されたがなんらかの問題が発生したとき(SQLCODE の値が 1 から
99)でも無視されます。なお、通常は、警告エラーは検査するのが原則です。
重要
エラー処理ルーチンの先頭に WHENEVER SQLERROR CONTINUE を記述することで、
エラー(処理失敗)に対する処理を一時的に無効にすることができます。ただし、この文
は 必 ず、エ ラ ー 処 理 ル ー チンの前に記述しなければなりません。
WHENEVER
SQLERROR CONTINUE をエラー処理ルーチンの後に記述した場合、エラー処理ルーチ
ン(の SQL 文)でエラーが発生すると、エラー処理ルーチン自体が呼び出され、無限ルー
プに陥ることがあります。
WHENEVER 文のスコープ
WHENEVER は、その WHENEVER を記述してあるモジュール(ソースコードファイル)
内の後続の SQL 文に対してだけ有効です。したがって、複数のモジュールで構成されるプ
ログラムでは、各モジュールで WHENEVER 文を記述しなければなりません。
エラー処理ルーチンの変更
エ ラ ー の 内 容(SQLERROR、SQLWARNING、NOT
FOUND)が同じであれば、
WHENEVER をもう 1 つ埋め込み、ジャンプ先を指定することで、これまでのエラー処理
ルーチンを別の処理ルーチンに変更することができます。この場合、それまでのエラー処理
ルーチンは無効になります。たとえば、次の文は、エラーの内容が SQLERROR で最初の
ジャンプ先が ErrorExit1 でしたが、その後、ジャンプ先を ErrorExit2 に変更している例で
す。
EXEC SQL
第 12 章 エラー処理と回復
12-3
標準のエラー処理
WHENEVER SQLERROR
GOTO ErrorExit1;
. . .
EXEC SQL
WHENEVER SQLERROR
GOTO ErrorExit2;
. . .
WHENEVER 文に関する制限事項
WHENEVER には、次のような 2 つの制限があります。
• プログラムとは直接関係のないエラーまでトラップしてしまうことがあります。 たとえ
ば、WHENEVER SQLERROR では CHECK 制約に反するデータ入力がトラップされま
すが、この他必要なデータベースが見つからないときにもエラーとなります。この場合、
どちらも同一のエラー処理ルーチンにジャンプしてしまいます。無効なデータ入力はタイ
ピングのミスによるもので、データの再入力で対応できますが、データベースの検索不能
は致命的なエラーで、プログラムの外で措置を取らなければなりません。
• エラーが発生した場所で処理を再開する場合は、記述が難しくなります。たとえば、1 つの
WHENEVER SQLERROR を使用して、CHECK 制約に反するデータ入力をプログラム
の各所でトラップできますが、
どの場合も同一のエラー処理ルーチンにジャンプします。こ
のような場合、ユーザーにとってはデータの再入力が便利ですが、1 つのエラー処理ルー
チンでは、エラーの発生した場所にジャンプして処理を再開するのは、容易ではありませ
ん。
もちろん、複雑なエラー処理ルーチンを書くこともできます。たとえば、C や C++ では、
手の込んだ CASE 文を使って SQLCODE を直接検査し、その値にしたがって細かく対応
することができます。それでも、エラーが発生した場所で処理を再開するという記述は簡単
ではありません。このため、エラーの発生場所で処理を再開する場合は、通常、SQL 文の
たびに SQLCODE を直接検査するか、複数のエラー処理ルーチンを組み合わせるという方
法を使用します。
SQLCODE の直接検査
WHENEVER では発生するエラーをすべてトラップすることができますが、このほか、
SQL 文の後で SQLCODE を直接検査することもできます。この方法の主な利点としては、
個々の状況に応じてエラー処理ルーチンを自由に設計できるという点が挙げられます。
たとえば、次の C コードは、SELECT 文の実行後に、SQLCODE の内容を検査する例で
す。この検査で SQLCODE が 0(処理に成功)以外の場合、if 句の中の文が実行されま
す。ここでは、if 文は 2 つあり、それぞれ SQLCODE が -1 であるかどうか、また 100 で
あるかどうかが検証されます。
SQLCODE が -1 または 100 以外の場合は、エラーメッセージが表示され、プログラムは
終了します。
EXEC SQL
SELECT CITY INTO :city FROM STATES
WHERE STATE = :stat:statind;
12-4 埋 め 込 み S Q L ガ イ ド
標準のエラー処理
if (SQLCODE)
{
if (SQLCODE == –1)
printf("too many records found¥n");
else if (SQLCODE == 100)
printf("no records found¥n");
else
{
printf("Database error, SQLCODE = %d¥n", SQLCODE);
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT;
exit(1);
}
}
printf("found city named %s¥n", city);
EXEC SQL
COMMIT;
EXEC SQL
DISCONNECT;
SQLCODE の直接検査の場合、エラーの発生を検出するために SQL 文ごとに多数のコー
ドが必要で、これが短所でもあります。一方、記述さえしておけば、関数呼び出しを使って
エラーを処理することもできます。次の C のコードはその例です。
EXEC SQL
SELECT CITY INTO :city FROM STATES
WHERE STATE = :stat:statind;
switch (SQLCODE)
{
case 0:
break; /* エラーなし */
case –1
ErrorTooMany();
break;
case 100:
ErrorNotFound();
break;
default:
ErrorExit(); /* その他のエラーを処理します */
break;
}
. . .
エラー処理に関数呼び出しを使った場合、エラーが修正できるものであれば、実行の再開
を行うこともできます。
第 12 章 エラー処理と回復
12-5
標準のエラー処理
WHENEVER 文と直接検査の組み合わせ
WHENEVER と SQLCODE の直接検査を組み合わせて使用すると、ほとんどのプログラ
ムで有効なエラー処理が可能です。この方法では、通常のエラーに対しては WHENEVER
文で対処し、特に重要な場面では WHENEVER を一時的に無効にし、SQLCODE の直接
検査を行います。
この方法では、C コードは次のようになります。
• WHENEVER 文を使って一般的なエラーに対処します。
• CONTINUE 句を使って WHENEVER SQLERROR を無効にしてから、処理を続行しま
す。
• SQLCODE を直接検査します。
• 無効にした WHENEVER SQLERROR を再度有効にします。
main()
{
EXEC SQL
WHENEVER SQLERROR GOTO ErrorExit; /* すべてのエラーをトラップします
*/
EXEC SQL
WHENEVER SQLWARNING GOTO WarningExit; /* 警告をトラップします */
EXEC SQL
WHENEVER NOT FOUND GOTO AllDone; /* EOF をトラップします */
. . .
EXEC SQL
WHENEVER SQLERROR CONTINUE; /* エラーのトラップを抑制します */
EXEC SQL
SELECT CITY INTO :city FROM STATES
WHERE STATE = :stat:statind;
switch (SQLCODE)
{
case 0:
break; /* エラーなし */
case –1
ErrorTooMany();
break;
case 100:
ErrorNotFound();
break;
default:
ErrorExitFunction(); /* その他のエラーを処理します */
break;
}
EXEC SQL
WHENEVER SQLERROR GOTO ErrorExit; /* リセットして、すべてのエラーを
トラップします */
. . .
12-6 埋 め 込 み S Q L ガ イ ド
標準のエラー処理
}
エラー処理のガイドライン
プログラムでエラー処理ルーチンを記述する場合、次のようなガイドラインが適用されま
す。
SQL 文とホスト言語文
エラー処理ルーチンでは、CONNECT を除き、SQL 文と InterBase の関数はすべて記述で
きます。
エラー処理ルーチンでは、ホスト言語文と関数はすべて記述でき、使用上の制限もありま
せん。
重要
エラー処理ルーチンの先頭に WHENEVER SQLERROR CONTINUE を記述することで、
エラー(処理失敗)に対する処理を一時的に無効にすることができます。ただし、この文
WHENEVER
は 必 ず、エ ラ ー 処 理 ル ー チンの前に記述しなければなりません。
SQLERROR CONTINUE をエラー処理ルーチンの後に記述した場合、エラー処理ルーチ
ン(の SQL 文)でエラーが発生すると、エラー処理ルーチン自体が呼び出され、無限ルー
プに陥ることがあります。
ネストされたエラー処理ルーチン
エラー処理ルーチンはネストして記述したり、再帰的呼び出すことができます。ただし、
SQLCODE と InterBase エラーステータス配列の内容が保持できない場合は、このような
記述は避けた方が無難です。
予期しない、または復旧不能のエラーの処理
エラー処理ルーチンでは、通常、復旧可能なエラーについて検査と処理を行います。この
ほか、予期しないか復旧不能なエラーに対しても必ず対応しなければなりません。
このような復旧不能なエラーは、次のコードで対処できます。
. . .
isc_print_sqlerr(SQLCODE, isc_status);
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT;
exit(1);
移植性
InterBase 以外の SQL プログラムとの移植性を考えた場合、エラー処理は WHENEVER 文
または SQLCODE の値の直接検査による処理に限定するのが賢明です。
第 12 章 エラー処理と回復
12-7
InterBase 独自のエラー処理
InterBase では、通常の SQLCODE に比べて、エラーの内容が内部でさらに細かく認識さ
れます。つまり、SQLCODE の値が 1 つであっても、その値が複数の種類の内部エラーを
示すこともあります。移植性が問題でなければ、通常のエラー処理に加えて、InterBase 独
自のエラー処理を行うのも有効です。この章の以降のトピックでは、InterBase 独自のエ
ラー処理について説明します。
InterBase 独自のエラー処理
InterBase では、同じ SQLCODE の値が数種類のエラーを意味することもあります。たと
えば、種類の異なるさまざまな InterBase のエラーに、–901 という SQLCODE の値が返
されます。したがって、他のベンダーの SQL プログラムとの移植性が必要とされない場合
は、プログラムでエラーを細かく検査することもできます。この検査には、InterBase のエ
ラーステータス配列である isc_status を使用します。
isc_status は、ISC_STATUS 型の配列で要素の数は 20 です。宣言は、gpre による前処理
時に自動的に行われます。isc_status では、次の 2 種類のエラー情報が報告されます。
• InterBase のエラーメッセージ
• InterBase のエラー番号
SQLCODE の値が –1、0、100 以外の場合、エラー処理ルーチンでは isc_status を使って
次のような処理を行うことができます。
• SQL のエラーメッセージと InterBase のエラーメッセージの表示
• SQL のエラーメッセージと InterBase のエラーメッセージをバッファへ取り込み、その後
の処理に利用
• 特定の InterBase のエラーコードのトラップと対応処理
エラーステータス配列である isc_status は、通常、WHENEVER または SQLCODE の直
接検査によるエラーのトラップの後で、その内容を検査します。
エラーメッセージの表示
SQLCODE が –1 未満の値の場合、その値に対応する InterBase のエラーメッセージを表
示できます。この表示は、エラー処理ルーチンの中に InterBase の isc_print_sqlerror() 関
数を記述することで可能です。なお、isc_print_sqlerror() 関数の宣言は、gpre によるアプ
リケーションの前処理時に自動的に行われます。
isc_print_sqlerror() では、SQLCODE の値、その値に対応する SQL のエラーメッセージ、
エラーステータス配列である isc_status に格納されている InterBase のエラーメッセージ
が表示されます。パラメータは、SQLCODE と isc_status へのポインタの 2 つです。
たとえば、次は isc_print_sqlerror() によるエラー処理ルーチンの例で、エラーが発生した
場合、SQLCODE の値、その値に対応する SQL のエラーメッセージ、InterBase のエラー
メッセージ(メッセージがある場合)が表示されます。
. . .
EXEC SQL
SELECT CITY INTO :city FROM STATES
12-8 埋 め 込 み S Q L ガ イ ド
InterBase 独自のエラー処理
WHERE STATE = :stat:statind;
if(SQLCODE)
{
isc_print_sqlerror(SQLCODE, isc_status);
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT ALL;
exit(1);
}
. . .
重要
ウィンドウを使用するシステムによっては、画面への直接書き込みは推奨されていないか
禁止されていることがあります。このようなシステムでは、isc_print_sqlerror() の使用は
避けます。この場合、かわりに、isc_sql_interprete() や isc_interprete() を使用して、メッ
セージをバッファに取り込んだ後、表示します。
SQL エラーメッセージの取り込み
SQL のエラーメッセージは isc_print_sqlerror() を使って直接表示できますが、このほか、
isc_sql_interprete() を使ってバッファに取り込むこともできます。このエラーメッセージ
の取り込みは、次のような場合に行います。
• 画面への直接書き込みができないウィンドウシステムでアプリケーションを動作させる場
合
• アプリケーションで、すべてのエラーメッセージをログファイルに保存する場合
• エラーメッセージを操作またはフォーマットして表示する場合
isc_sql_interprete() では、SQLCODE、メッセージを格納するバッファへのポインタ、バッ
ファの最大サイズ(バイト数)の 3 つの引数を指定します。これで、SQL のエラーメッ
セージを表す文字列が作成され、その文字列がフォーマットされるとともにバッファに格
納されます。バッファのエラーメッセージは、その後操作が可能になります。なお、バッ
ファサイズは、256 バイトあれば、通常 SQL のエラーメッセージを十分格納できます。
たとえば、次は、SQL のエラーメッセージを err_buf に格納した後、err_buf の内容をロ
グファイルに書き込むコードの例です。
. . .
char err_buf[256]; /* isc_sql_interprete() のエラーメッセージバッファ */
FILE *efile=NULL; /* このコードでは、開かれているファイルへのポインタであると
します */
. . .
EXEC SQL
SELECT CITY INTO :city FROM STATES
WHERE STATE = :stat:statind;
if (SQLCODE)
{
isc_sql_interprete(SQLCODE, err_buf, sizeof(err_buf));
第 12 章 エラー処理と回復
12-9
InterBase 独自のエラー処理
if(efile==NULL) efile=fopen("errors", "w");
fprintf(efile, "%s¥n", err_buf); /* バッファの内容をログファイルに書き込
みます */
EXEC SQL
ROLLBACK; /* データベースへの変更を元に戻します */
EXEC SQL
DISCONNECT ALL; /* 開かれているデータベースを閉じます */
exit(1); /* エラーフラグを設定して終了します */
}
. . .
上記のように、isc_sql_interprete() を使用して、SQLCODE の値に対応するエラーメッ
セージを取り出してフォーマットすることができます。一方、SQLCODE が –1 未満の値
の 場 合、InterBase 独 自 のエラーメッセージを取り出すこともできます。 この場合、
isc_interprete() 関数を使用します。ここでも、エラーメッセージは同じくフォーマットさ
れてバッファに格納されます。
InterBase エラーメッセージの取り込み
InterBase のエラーメッセージは、isc_interprete() を使ってバッファに取り込むことができ
ます。isc_interprete() は、次のような場合に使用します。
• 画面への直接書き込みができないウィンドウシステムでアプリケーションを動作させる場
合
• アプリケーションで、すべてのエラーメッセージをログファイルに保存する場合
• エラーメッセージを操作またはフォーマットして表示する場合
重要
isc_interprete() は、SQLCODE が –1 未満の値である場合にのみ使用するようにします。
これは、SQLCODE が -1 の場合、isc_status のエラー情報が不正確になることがあるため
です。
isc_interprete() では、InterBase のエラーメッセージの格納に使用するバッファの場所(割
り当て済み)と、isc_status エラーステータス配列の先頭へのポインタの 2 つの引数を指定
します。これで、isc_status から InterBase のエラーメッセージを表す文字列が作成され、
その文字列がフォーマットされるとともにバッファに格納されます。バッファのエラー
メッセージは、その後の操作が可能です。また、isc_status の先頭を指すポインタが、エ
ラー情報を表す次のクラスタへ進められます。
isc_interprete() の場合、1 回の呼び出しで取り出され、フォーマットされるエラーメッセー
ジは 1 つです。一方、プログラムで発生するエラーによっては、isc_status に複数のエラー
メッセージが格納されることもあります。したがって、関連するエラーメッセージをすべて
取 り 出 す と き は、エ ラ ー 処 理 ル ー チ ン で、エ ラ ー メ ッ セ ー ジ が 返 ら な く な る ま で
isc_interprete() を繰り返し呼び出すことが必要です。
また、isc_interprete() は isc_status へのポインタを制御することから、直接 isc_status を
渡すことはできません。このため、isc_status へのポインタを宣言し、そのポインタを
isc_interprete() に渡すという手順が必要です。
12-10 埋 め 込 み S Q L ガ イ ド
InterBase 独自のエラー処理
次は、InterBase のエラーメッセージを取り出してログファイルに保存する C のコードの例
です。main() の先頭では、文字列のバッファと isc_status へのポインタの宣言を行ってい
ます。なお、ここでは、main() でログファイルが宣言されて開かれており、その後、制御
がエラー処理ルーチンに渡されるものとします また、後半のエラー処理ルーチンでは、
isc_status の先頭にポインタを設定した後、isc_interprete() を呼び出しています。
. . .
#include "ibase.h";
. . .
main()
{
char msg[512];
ISC_STATUS *vector;
FILE *efile; /* このコードでは、開かれているファイルへのポインタであるとします
*/
. . .
if (SQLCODE < –1)
ErrorExit();
}
. . .
ErrorExit()
{
vector = isc_status; /* ステータスベクターの先頭に(再)設定します */
isc_interprete(msg, &vector); /* 最初のメッセージを取得します */
fprintf(efile, "%s¥n", msg); /* バッファの内容をログファイルに書き込みま
す */
msg[0] = '-'; /* 2 番めのメッセージの先頭にハイフンを追加します */
while (isc_interprete(msg + 1, &vector)) /* まだあるか ?*/
fprintf(efile, "%s¥n", msg); /* ある場合はログに書き込みます */
fclose(efile); /* ログを閉じてからプログラムを終了します */
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT ALL;
exit(1); /* 「異常終了」コードを設定してプログラムを終了します */
}
. . .
上の例の処理を順にまとめてみると、次のようになります。
• isc_status エラーステータス配列へのポインタを、isc_status の先頭(ステータスベクター
の先頭のアドレス)に設定します。
• isc_interprete() に対する 1 回めの呼び出しを行い、isc_status(ステータスベクター)の最
初のエラーメッセージを取り出します。
• 最初のエラーメッセージをログファイルに書き込みます。
第 12 章 エラー処理と回復
12-11
InterBase 独自のエラー処理
• WHILE ループで isc_interprete() を繰り返し呼び出し、残りのエラーメッセージを取り出
します。残りのエラーメッセージがあったときは、そのエラーメッセージをログファイル
に書き込みます。
• トランザクションによる処理をロールバックします。
• データベースを閉じて、システムリソースを解放します。
InterBase エラーコードの処理
SQLCODE が –1 未満の値の場合、エラーステータス配列 isc_status には InterBase 独自のエ
ラーメッセージが格納されます。この中には InterBase のエラーコード(isc_convert_error な
ど)も含まれています。InterBase のエラーコードは、それぞれ InterBase 独自のエラーメッ
セージを示す番号に対応しています。この InterBase のエラーコードは、エラー処理ルーチン
でトラップすることができ、また個別に対処することもできます。ただし、このエラーコー
ドの扱いには注意が必要です。
InterBase のエラーコードをトラップして対処する場合、エラー処理ルーチンでは次の手順
で処理を行います。
1 SQLCODE を検査し、–1 未満の値であることを確認します。
2 エラーステータス配列(isc_status)の最初の要素が isc_arg_gds に設定されているこ
とを確認します。InterBase のエラーコードは、最初の要素が isc_arg_gds に設定され
ている場合に限りトラップが可能です。なお、C 言語のプログラムでは、エラーステー
タス配列の最初の要素は isc_status[0] となります。
エラーステータス配列の最初の要素の値(初期値)が 1 以外の場合は、そのエラース
テータス配列により返されるエラーの処理は行わないでください。
3 SQLCODE が –1 未満の値で、かつ isc_status の最初の要素が isc_arg_gds に設定され
ていれば、isc_status の 2 番めの要素(isc_status[1])の中の InterBase のエラーコー
ドを使って分岐し、エラーに対応するためのルーチンを記述します。
ヒント
InterBase のエラーコードは、ニーモニックとして定義(たとえば、isc_arg_gds)されて
いるため、コードでも解読、理解、維持が簡単です。InterBase のエラーコードの定義は
ibase.h ファイルにあります。
エラー処理の例を示す C コード
• isc_print_sqlerror() を使ってエラーメッセージを表示します。
• 6 つの InterBase のエラーのトラップ、取り消し、データ入力、再実行を行います。
• InterBase のエラーコードとして定義されているニーモニックを使って記述を行います。
. . .
int c, jval, retry_flag = 0;
jmp_buf jumper;
. . .
main()
{
. . .
jval = setjmp(jumper);
12-12 埋 め 込 み S Q L ガ イ ド
InterBase 独自のエラー処理
if (retry_flag)
ROLLBACK;
. . .
}
int ErrorHandler(void)
{
retry_flag = 0; /* 0 にリセットします。再試行しません */
isc_print_sqlerror(SQLCODE, isc_status); /* エラーを表示します */
if (SQLCODE < –1)
{
if (isc_status[0] == isc_arg_gds)
{
switch (isc_status[1])
{
case isc_convert_error:
case isc_deadlock:
case isc_integ_fail:
case isc_lock_conflict:
case isc_no_dup:
case isc_not_valid:
printf("¥n Do you want to try again? (Y/N)");
c = getchar();
if (c == 'Y' || c == 'y')
{
retry_flag = 1; /* 再試行フラグを設定します */
longjmp(jumper, 1);
}
break;
case isc_end_arg:/* 実際にはエラーがない場合 */
retry_flag = 1; /* 再試行フラグを設定します */
longjump(jumper, 1);
break;
default:/* すべては処理できないため中止します */
break;
}
}
}
EXEC SQL
ROLLBACK;
EXEC SQL
DISCONNECT ALL;
exit(1);
}
第 12 章 エラー処理と回復
12-13
12-14 埋 め 込 み S Q L ガ イ ド
第
章
動的 SQL
第 13 章
この章では、DSQL アプリケーションの記述方法について説明します。DSQL では、実行
時に SQL 文の作成と実行が可能です。
データベースアプリケーションでは、通常、プログラム作成時にデータベースに対して実
行する SQL 文を指定しておきます。この後、アプリケーションをコンパイルし、アプリ
ケーションの SQL 文が設定されます。一方アプリケーションによっては、実行時に用意し
た文字列、またはユーザーが入力した文字列をもとに SQL 文を実行できると便利です。こ
のようなアプリケーションでは、実行時に動的に SQL 文を作成したり実行できることが条
件となります。この能力を備えたアプリケーションを動的 SQL(DSQL)アプリケーショ
ンと呼んでいます。なお、InterBase のユーティリティである isql も DSQL アプリケーショ
ンです。
DSQL プログラミングプロセスの概要
DSQL 文の作成と実行は、基本的に次の手順で行います。
• DSQL 処理をサポートしている SQL 文をアプリケーションに埋め込みます。
• ホスト言語のデータ型(変数)やマクロなどを使用して、入力領域と出力領域を用意しま
す。実行時には、この領域(XSQLDA)を介して文とパラメータがやりとりされます。
• アプリケーションに埋め込んだ SQL 文や用意した入出力領域についてプログラミングを
行い、実行時の SQL 文が問題なく処理されるようにします。
これらのステップについて、以降の各節で詳しく説明します。
DSQL に関する制限事項
DSQL には数多くの利点がありますが、次のような制限もあります。
• 一度に 1 つのデータベースにしかアクセスできません。
第 13 章 動的 SQL
13-1
DSQL に関する制限事項
• 動的なトランザクション処理を行うことはできません。トランザクションは、コンパイル
時に宣言しておかなければなりません。
• BLOB データや配列データに動的にアクセスすることはできません。ただし、静的に扱わ
れる標準の SQL 文を使って BLOB データと配列データに対してアクセスすることは可能
です。または、低レベルの API 呼び出しでもアクセスできます。
• データベースの作成は、EXECUTE IMMEDIATE と CREATE DATABASE の組み合わ
せによる処理に限られます。
DSQL アプリケーションでのトランザクション操作の詳細は、13-3 ページの「トランザク
ションの操作」を参照してください。DSQL での BLOB データの処理の詳細は、13-4 ペー
ジの「BLOB データの処理」を参照してください。DSQL での配列データの詳細は、13-4
ページの「配列データの処理」を参照してください。データベースの動的作成の詳細は、
13-4 ページの「データベースの作成」を参照してください。
データベースへのアクセス
標準 SQL 構文では、DSQL アプリケーションは 1 つのソースファイルモジュールについ
て 1 つのデータベースハンドルしか使用できません。言い換えれば、DSQL アプリケーショ
ンでは一度に 1 つのデータベースにしか接続できません。また、データベースハンドルは、
gpre によるアプリケーションの前処理に先立ち、その宣言と初期化を行っておかなければ
なりません。たとえば、次のコードでは、db1 というデータベースハンドルが宣言されると
ともに、0 に初期化されます。
#include "ibase.h"
isc_db_handle db1;
. . .
db1 = 0L;
宣言と初期化が終わると、実行時に、そのデータベースハンドルをデータベースに動的に
割り当てることができます。次は、実行時の割り当ての例です。
char dbname[129];
. . .
prompt_user("Name of database to open: ");
gets(dbname);
EXEC SQL
SET DATABASE db1 = :dbname;
EXEC SQL
CONNECT db1;
. . .
DSQL 文でアクセスできるのは、直前の SET DATABASE コマンドで指定されたデータ
ベース(データベースハンドルを使って指定)に限られます。なお、それまで接続していた
データベースを DISCONNECT を使って接続解除すれば、同じデータベースハンドルを
使って別のデータベースに接続できます。この場合、DISCONNECT により自動的にデー
タベースハンドルが NULL に設定されます。たとえば、次は、それまで接続していたデー
タベースを DISCONNECT で接続解除した後、0 に初期化し、別のデータベースに接続す
る場合の記述例です。
13-2 埋 め 込 み S Q L ガ イ ド
DSQL に関する制限事項
EXEC SQL
DISCONNECT db1;
EXEC SQL
SET DATABASE db1 = 'employee.ib';
EXEC SQL
CONNECT db1;
また、複数のデータベースごとにソースファイルモジュールを作成するという方法を使用
すると、DSQL でも複数のデータベースに接続できます。この場合、データベースへの接
続とデータのアクセスには、低レベルの API 呼び出しを使用します。API 呼び出しの詳細
は、『API ガイド』を参照してください。データベースで使用される SQL 文の詳細は、第
3 章「データベースの操作」を参照してください。
トランザクションの操作
InterBase では、トランザクション名はすべて gpre によるアプリケーションの前処理時に
宣言が済んでいなければなりません。また、トランザクション名は前処理により設定され、
実行時に変更することはできません。さらに、実行時にトランザクション名を動的に宣言
することもできません。
PREPARE、DESCRIBE、EXECUTE、EXECUTE IMMEDIATE などの SQL 文は、オプ
ションで TRANSACTION 句を付加できます。このような SQL 文では、再コンパイルに
先立ち、TRANSACTION 句でトランザクション名を記述しておくことで、実行時の DSQL
文とともに機能するトランザクションを指定することができます。たとえば、次は、t1 と
いうトランザクションを宣言、初期化するとともに、PREPARE に TRANSACTION 句を
付加して t1 を指定しているコード例です。実行時の DSQL 文(この例では、PREPARE に
より処理される DSQL 文)では、このトランザクションが機能することになります。
#include "ibase.h"
isc_tr_handle t1;
. . .
t1 = 0L;
EXEC SQL
SET TRANSACTION NAME t1;
EXEC SQL
PREPARE TRANSACTION t1 Q FROM :sql_buf;
PREPARE、DESCRIBE、EXECUTE、EXECUTE IMMEDIATE のいずれかにより処理
される DSQL 文では、その文の中に TRANSACTION 句を使用することはできません。も
ちろん、上の例のように、標準の埋め込み SQL では TRANSACTION 句を記述できます。
SET TRANSACTION 文は、PREPARE、DESCRIBE、EXECUTE を使って準備、実行
することはできませんが、EXECUTE IMMEDIATE を使って処理することは可能です。た
だし、次のような条件があります。
1
2
それまでのトランザクションがコミット(またはロールバック)済みであること
トランザクションハンドルが NULL に設定されていること
第 13 章 動的 SQL
13-3
DSQL に関する制限事項
この場合の記述例は次のようになります。ここでは、まず、それまでのデフォルトトラン
ザ ク シ ョ ン を コ ミ ッ ト し て NULL に設定した後、EXECUTE IMMEDIATE で SET
TRANSACTION を実行(新規の READ ONLY のトランザクションを起動)しています。
EXEC SQL
COMMIT;
/* デフォルトのトランザクション名を NULL に設定します */
gds__trans = NULL;
EXEC SQL
EXECUTE IMMEDIATE 'SET TRANSACTION READ ONLY';
データベースの作成
DSQL アプリケーションでは、次の手順で新規のデータベースを作成することができます。
1
現在接続されているデータベースをすべて接続解除します。この接続解除により、デー
タベースハンドルは自動的に NULL に設定されます。
2 処理に使用する CREATE DATABASE 文を用意します。
3 CREATE DATABASE 文を EXECUTE IMMEDIATE で実行します。
たとえば、次の文は、現在接続されているデータベースをすべて接続解除し、新規のデー
タベースを作成する例です。データベースハンドルは接続解除により NULL に設定される
ため、そのデータベースを使って後続の DSQL 文で新規のデータベースの作成が可能で
す。
char *str = "CREATE DATABASE ¥"new_emp.ib¥"";
. . .
EXEC SQL
DISCONNECT ALL;
EXEC SQL
EXECUTE IMMEDIATE :str;
BLOB データの処理
DSQL では、BLOB データを直接処理することはできません。DSQL では BLOB カーソ
ルがサポートされていないためです。ただし、DSQL アプリケーションでも、API 呼び出
しを使って BLOB データを処理することができます。BLOB データを処理する API 呼び
出しの詳細は、『API ガイド』を参照してください。
配列データの処理
DSQL では、配列データを直接処理することはできません。ただし、DSQL アプリケーショ
ンでも API 呼び出しを使って配列データを処理することができます。API 呼び出しの詳細
は、『API ガイド』を参照してください。
13-4 埋 め 込 み S Q L ガ イ ド
DSQL アプリケーションの記述
DSQL アプリケーションの記述
DSQL アプリケーションは、一般に、実行時まで次の事項が不明な場合に記述します。
• SQL 文で使用する文字列
• 変数の数
• 変数のデータ型
• データベースオブジェクトへの参照
DSQL アプリケーションの記述は、通常の SQL アプリケーションに比べて複雑になるのが
普通です。これは、DSQL の処理のほとんどが、拡張 SQL デスクリプタエリア(XSQLDA)
デ ー タ 構 造 体 の 明 示 的 な 割 り当てと処理が必要になるためです。DSQL では、この
XSQLDA を介してデータベースとデータのやりとりが行われます。
DSQL で SQL 文を処理する場合、次のような手順で行います。
1
2
3
4
使用する SQL 文が DSQL で処理できるかどうかを検査します。
アプリケーションで SQL 文を文字列として指定する。
必要に応じて、入力パラメータと戻り値に使用する XSQLDA を割り当てる。
適切な DSQL プログラミング手法を使用して、SQL 文を処理します。
DSQL で処理可能な SQL 文
DSQL では、ほとんどの SQL 文の処理が可能です。DSQL で使用可能な SQL 文を以下に
示します。
ALTER DATABASE
ALTER DOMAIN
ALTER EXCEPTION
ALTER INDEX
ALTER PROCEDURE
ALTER TABLE
ALTER TRIGGER
COMMIT
CONNECT
CREATE DATABASE
CREATE DOMAIN
CREATE EXCEPTION
CREATE GENERATOR
CREATE INDEX
CREATE PROCEDURE
CREATE ROLE
CREATE SHADOW
CREATE TABLE
CREATE TRIGGER
CREATE VIEW
DECLARE EXTERNAL FUNCTION
DECLARE FILTER
DELETE
DROP DATABASE
DROP DOMAIN
DROP EXCEPTION
DROP EXTERNAL FUNCTION
DROP FILTER
DROP INDEX
DROP PROCEDURE
DROP ROLE
DROP SHADOW
DROP TABLE
DROP TRIGGER
DROP VIEW
EXECUTE PROCEDURE
第 13 章 動的 SQL
13-5
DSQL アプリケーションの記述
GRANT
INSERT
INSERT CURSOR (BLOB)
REVOKE
ROLLBACK
SELECT
SET GENERATOR
UPDATE
DSQL で は、 CLOSE、DECLARE、CURSOR、DESCRIBE、EXECUTE、EXECUTE
IMMEDIATE、FETCH、OPEN、PREPARE の ESQL 文を使用できません。
DSQL では、BLOBDUMP、EDIT、EXIT、HELP、INPUT、OUTPUT、QUIT、SET、SET
AUTODDL、SET BLOBDISPLAY、SET COUNT、SET ECHO、SET LIST、SET NAMES、
SET PLAN、SET STATS、SET TERM、SET TIME、SHELL、SHOW CHECK、SHOW
DATABASE、SHOW DOMAINS、SHOW EXCEPTIONS、SHOW FILTERS、SHOW
FUNCTIONS、SHOW GENERATORS、SHOW GRANT、SHOW INDEX、SHOW
PROCEDURES、SHOW SYSTEM、SHOW TABLES、SHOW TRIGGERS、SHOW
VERSION、SHOW VIEWS の isql コマンドを使用できません。
SQL 文の文字列
DSQL アプリケーションでは、SQL 文が通常の SQL アプリケーションとは異なります。
つまり、DSQL アプリケーションでは、ユーザーがプロンプトで入力した SQL 文が直接
DSQL アプリケーションに取り込まれます。これは、isql の場合も同じです。このほか、
ユーザーとの対話による応答として、アプリケーションで生成されることもあります(こ
の場合、アプリケーションで SQL 文を文字列として記述することが必要)。DSQL では
SQL 文にかかわらず、SQL 文の文字列として指定しなければならず、この文字列が DSQL
に渡され処理が行われることになります。
SQL 文を表す文字列は C の文字列で、DSQL で直接処理されます。したがって、先頭の
EXEC SQL 接頭辞は不要で、末尾にセミコロン(;)を付ける必要もありません。ただし、
C の文字列宣言のターミネータであるセミコロンは付加しなければなりません。たとえば、
次は、SQL 文を文字列として指定している変数宣言の例です。
char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = 256";
SQL 文の文字列の値パラメータ
SQL 文の文字列の中には、式の中で評価の対象となる 1 つの数値または文字の値がパラ
メータとして含まれるものがあります。こういったパラメータは、文字列として指定する
SQL 文の任意の場所に記述できます。ただし、値がデータベースオブジェクトの名前とし
て解釈されるような場所には記述できません。
SQL 文の文字列の中のパラメータは、実行時に定数として、またはプレースホルダとして
渡すこともできます。たとえば、次の文字列は、パラメータである 256 を定数として渡す
例です。
char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = 256";
13-6 埋 め 込 み S Q L ガ イ ド
XSQLDA
上の例では定数は 256 だけですが、パラメータを持つ式を AND などを使って複数組み合
わせて記述することもできます。この記述方法は、式の値引数が定数ばかりでなく列名の場
合、またはその SQL 文をアプリケーションで 1 回しか実行しない場合などに役立ちます。
パラメータをプレースホルダとして渡す場合は、次のようにステートメント文字列に埋め
込んだ疑問符(?)として値を渡します。
char *str = "DELETE FROM CUSTOMER WHERE CUST_NO = ?";
プレースホルダが記述されている SQL 文が DSQL で処理される場合、疑問符は XSQLDA
に格納されている値に置換されます。したがって、プレースホルダを使って SQL 文を用意
しておくと、その後は値を変えて何度でもその SQL 文を実行することができます。
プレースホルダは、通常、WHERE 句の検索条件や UPDATE 文の SET 句で使用します。
XSQLDA
DSQL アプリケーションでは、拡張 SQL デスクリプタエリア(XSQLDA)を宣言しなけ
ればなりません。XSQLDA 構造体は、InterBase の include ディレクトリにある ibase.h
ヘッダーファイルに定義されています。アプリケーションでは、使用する XSQLDA のイ
ンスタンスを宣言する必要があります。
XSQLDA はホスト言語のデータ構造体で、DSQL で SQL 文が処理される際は、この構造
体を介してデータのデータベースへの移動、またはデータのデータベースからの移動が行
われます。XSQLDA には、入力デスクリプタ(入力 XSQLDA)と出力デスクリプタ(出
力 XSQLDA)の 2 種類があります。どちらのデスクリプタも XSQLDA 構造体で、構造は
同じです。
XSQLDA には XSQLVAR というフィールドがあります。この XSQLVAR は非常に重要
なフィールドです。アプリケーションでは、この XSQLVAR が入力パラメータまたは戻り
値(返される列のデータ)の処理に必ず使用されるためです。XSQLVAR も XSQLDA と
同様に、それ自体が構造体で、InterBase の include ディレクトリの中の ibase.h ヘッダー
ファイルに定義されています。
アプリケーションでは、事前に XSQLVAR のインスタンスを宣言しておく必要はありませ
ん。ただし、DSQL 文を実行する前に、必要とされる数の XSQLVAR 構造体用の格納領
域を動的に割り当てなければなりません。割り当てた格納領域は、DSQL 文の実行後、必
要に応じて解放します。
次の図は、XSQLDA と XSQLVAR の関係を示したものです。
第 13 章 動的 SQL
13-7
XSQLDA
図 13.1
XSQLDA と XSQLVAR の関係
XSQLDA の 1 つのインスタンス
short version
char sqldaid[8]
ISC_LONG sqldabc
short sqln
short sqld
XSQLVAR sqlvar[1]
XSQLVAR の n 個のインスタンスの配列
1 番めのインスタンス
n 番めのインスタンス
short sqltype
short sqltype
short sqlscale
short sqlscale
short sqlprecision
short sqlprecision
short sqlsubtype
short sqlsubtype
short sqllen
short sqllen
char *sqldata
char *sqldata
short *sqlind
short *sqlind
short sqlname_length
short sqlname_length
char sqlname[METADATALENGTH]
char sqlname[METADATALENGTH]
short relname_length
short relname_length
char relname[METADATALENGTH]
char relname[METADATALENGTH]
short ownname_length
short ownname_length
char ownname[METADATALENGTH]
char ownname[METADATALENGTH]
short aliasname_length
short aliasname_length
char aliasname[METADATALENGTH]
char aliasname[METADATALENGTH]
入力 XSQLDA は、1 つの XSQLDA 構造体と、入力パラメータと同じ数の XSQLVAR 構
造体で構成されます。出力 XSQLDA も、1 つの XSQLDA 構造体と、1 つの SQL 文で返
されるデータと同じ数の XSQLVAR 構造体で構成されます。XSQLDA とその XSQLDA
に含まれる XSQLVAR 構造体は、メモリ内の連続した領域に一括して割り当てられます。
13-8 埋 め 込 み S Q L ガ イ ド
XSQLDA
XSQLVAR 構造体の数は、PREPARE 文または DESCRIBE 文で判定できます。また、
XSQLDA と XSQLVAR の格納に必要なメモリ領域は、XSQLDA_LENGTH マクロで割
り当てることができます。XSQLDA_LENGTH マクロの詳細は、13-12 ページを参照し
てください。
XSQLDA のフィールド
次の表に、XSQLDA 構造体を構成するフィールドその説明を示します。
表 13.1 XSQLDA のフィールド
フィールド定義
説明
short version
XSQLDA 構造体のバージョンを示す。これは
SQLDA_CURRENT_VERSION に設定する。この値は ibase.h で定義されて
いる
char sqldaid[8]
予約
ISC_LONG sqldabc
予約
short sqln
sqlvar 配列の要素(XSQLVAR 構造体)の数。アプリケーションで設定する。
アプリケーションで XSQLDA 格納用のメモリ領域を割り当てるときは、必
ずこのフィールドを設定しなければならない(XSQLDA_LENGTH マクロ
を使用する)
short sqld
入力パラメータの数(入力 XSQLDA の場合)または取り出されるデータの
。PREPARE または DESCRIBE の実行時に
数(出力 XSQLDA の場合)
InterBase によって設定される
入力 XSQLDA では、sqld が 0 の場合、SQL 文の入力パラメータがないこと
を表す。出力 XSQLDA では、sqld が 0 の場合、SQL 文が SELECT 文ではな
いことを表す
XSQLVAR sqlvar
XSQLVAR 構造体の配列。配列の要素の数は、sqln フィールドで示される
XSQLVAR のフィールド
次の表に、XSQLVAR 構造体を構成するフィールドとその説明を示します。
第 13 章 動的 SQL
13-9
XSQLDA
表 13.2 XSQLVAR のフィールド
フィールド定義
説明
short sqltype
入力パラメータまたは取り出されるデータのデータ型を表す。
PREPARE または DESCRIBE の実行時に InterBase によって設
定される
short sqlscale
数値データ型(DECIMAL、NUMERIC)の小数点以下の桁数
を示す。小数点以下の桁数はマイナスの数値で表される。
PREPARE または DESCRIBE の実行時に InterBase によって設
定される
short sqlprecision
高精度数値データ型(DECIMAL、NUMERIC)の精度を示
す。PREPARE または DESCRIBE の実行時に InterBase によっ
て設定される
short sqlsubtype
BLOB データのサブタイプを示す。PREPARE または
DESCRIBE の実行時に InterBase によって設定される
short sqllen
sqldata フィールドのデータの最大サイズ(バイト数)。
PREPARE または DESCRIBE の実行時に InterBase によって設
定される
char *sqldata
入力 XSQLDA では、選択リスト項目またはパラメータのアド
レスを指定する。アプリケーションによって設定される
出力 XSQLDA では、選択リスト項目の値を指定する。
InterBase によって設定される
short *sqlind
入力 XSQLDA では、インジケータ変数のアドレスを指定する。
アプリケーションによって設定される
出力 XSQLDA では、FETCH で取り出される選択リスト項目
の列インジケータの値のアドレスを指定する
この値が 0 の場合、列のデータは NULL でないことを示す。–
1 の場合、列のデータは NULL であることを示す。InterBase
によって設定される
short sqlname_length
sqlname フィールドのデータのサイズ(バイト数)を示す。
DESCRIBE OUTPUT の実行時に InterBase によって設定される
char
sqlname[METADATALENGTH]
BLOB 列の名前を保持する配列を格納する
末尾に null 文字(¥0)は付加されない。DESCRIBE OUTPUT
の実行時に InterBase によって設定される
short relname_length
relname フィールドのデータのサイズ(バイト数)を示す。
DESCRIBE OUTPUT の実行時に InterBase によって設定される
char
relname[METADATALENGTH]
テーブルの名前を格納する
short ownname_length
ownname フィールドのデータのサイズ(バイト数)を示す。
DESCRIBE OUTPUT の実行時に InterBase によって設定される
13-10 埋 め 込 み S Q L ガ イ ド
末尾に null 文字(¥0)は付加されない。DESCRIBE OUTPUT
の実行時に InterBase によって設定される
XSQLDA
表 13.2 XSQLVAR のフィールド ( 続き )
フィールド定義
説明
char
ownname[METADATALENGTH]
テーブルのオーナー名を格納する
末尾に null 文字(¥0)は付加されない。DESCRIBE OUTPUT の
実行時に InterBase によって設定される
short aliasname_length
aliasname フィールドのデータのサイズ(バイト数)を示す。
DESCRIBE OUTPUT の実行時に InterBase によって設定される
char
aliasname[METADATALENGTH]
列のエイリアスを格納する。エイリアスが存在しない場合は、列
名を格納する
末尾に null 文字(¥0)は付加されない。DESCRIBE OUTPUT
の実行時に InterBase によって設定される
入力デスクリプタ
入力 XSQLDA(入力デスクリプタ)は、DSQL で処理する SQL 文の文字列にパラメータ
が含まれているときに使用します。これは、SQL 文にパラメータが含まれている場合は、
事前にパラメータに対して値を渡してから SQL 文を実行しなければならないためです。
パラメータがある場合、アプリケーションで DESCRIBE を使ってパラメータの数を指定
するとともに(この数は XSQLDA の sqld フィールドに格納されます)、各パラメータを
XSQLVAR 構造体に割り当てます。たとえば、次の SQL 文の文字列には 2 つのパラメー
タがあり、したがって、アプリケーションでは DESCRIBE を使ってこの数を指定し(sqld
が 2)、プレースホルダの XSQLVAR への割り当てなどの必要な処理を行います。
char *str = "UPDATE DEPARTMENT SET BUDGET = ? WHERE LOCATION = ?";
これで、SQL 文が実行されたときは、最初の XSQLVAR によって BUDGET 列の値が、2
番めの XSQLVAR によって LOCATION 列の値が InterBase に渡されることになります。
入力デスクリプタの詳細は、「DSQL のプログラミング手法」を参照してください。
出力デスクリプタ
出力 XSQLDA(出力デスクリプタ)は、クエリーによってその値がアプリケーションに返
される場合に使用します。この場合、戻り値の数は XSQLDA の sqld フィールドに格納さ
れます。戻り値は、それぞれ異なる XSQLVAR 構造体に格納されます。また、XSQLDA
の sqlvar フ ィ ー ル ド に は、最初の XSQLVAR 構造体が格納されます。 次は、出力
XSQLDA を必要とする SQL 文の例です。
char *str = "SELECT * FROM CUSTOMER WHERE CUST_NO > 100";
出力デスクリプタの使い方の詳細は、13-17 ページの「DSQL のプログラミング手法」を
参照してください。
第 13 章 動的 SQL
13-11
XSQLDA
XSQLDA_LENGTH マクロの使い方
XSQLDA_LENGTH マクロは ibase.h に定義されています。このマクロを使用して、入力
XSQLDA または出力 XSQLDA に割り当てられるメモリのバイト数を計算できます。
XSQLDA_LENGTH の定義は次のとおりです。
#define XSQLDA_LENGTH (n) (sizeof (XSQLDA) + (n - 1) *
sizeof(XSQLVAR))
n は、SQL 文の文字列の中のパラメータの数(入力 XSQLDA)、または、クエリーで返さ
れるデータの数(出力 XSQLDA)です。たとえば、次の C 言語の文は XSQLDA_LENGTH
マクロの使用例で、パラメータが 5 つの XSQLDA、または取り出されるデータの数が 5
つの XSQLDA に割り当てられるメモリの大きさ(バイト数)が算出されます。
XSQLDA *my_xsqlda;
. . .
my_xsqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(5));
. . .
XSQLDA_LENGTH マクロの使い方の詳細は、13-17 ページの「DSQL のプログラミン
グ手法」を参照してください。
SQL データ型マクロ定数
InterBase には、SQL のデータ型と NULL ステータスを示すマクロ定数が用意されていま
す。このマクロ定数を使用して、XSQLVAR のデータ型を表すことができます。アプリケー
ションでパラメータのデータ型を指定する場合、このマクロ定数を使用しなければなりま
せん。また、SQL 文で選択リストのデータ型を指定するときにも使用します。次の表は、
SQL のデータ型、そのデータ型に対応するマクロ定数、C のデータ型または InterBase の
typedef を一覧にしたものです。また、NULL または未知のデータを含むパラメータまた
は変数を示すために sqlind フィールドが使用されているかどうかについても示します。
表 13.3 SQL のデータ型、マクロ式、C のデータ型
sqlind
SQL データ型 マクロ式
C データ型または
typedef
が使用さ
れるか ?
配列
SQL_ARRAY
配列
SQL_ARRAY + 1
ISC_QUAD
はい
BLOB
SQL_BLOB
ISC_QUAD
いいえ
BLOB
SQL_BLOB + 1
ISC_QUAD
はい
BOOLEAN
SQL_BOOLEAN
符号付き short
いいえ
BOOLEAN
SQL_BOOLEAN + 1
符号付き short
はい
CHAR
SQL_TEXT
char[ ]
いいえ
CHAR
SQL_TEXT + 1
char[ ]
はい
13-12 埋 め 込 み S Q L ガ イ ド
いいえ
XSQLDA
表 13.3 SQL のデータ型、マクロ式、C のデータ型 ( 続き )
sqlind
SQL データ型 マクロ式
C データ型または
typedef
が使用さ
れるか ?
DATE
SQL_DATE
ISC_DATE
いいえ
DATE
SQL_DATE + 1
ISC_DATE
はい
DECIMAL
SQL_LONG、
SQL_DOUBLE、または
SQL_INT64
long、double、または
ISC_INT64
いいえ
DECIMAL
SQL_LONG + 1、
SQL_DOUBLE+ 1、または
SQL_INT64 + 1
long、double、または
ISC_INT64
はい
DOUBLE
PRECISON
SQL_DOUBLE
double
いいえ
DOUBLE
PRECISION
SQL_DOUBLE + 1
double
はい
INTEGER
SQL_LONG
long
いいえ
INTEGER
SQL_LONG + 1
long
はい
FLOAT
SQL_FLOAT
float
いいえ
FLOAT
SQL_FLOAT + 1
float
はい
NUMERIC
SQL_SHORT、SQL_LONG、
SQL_DOUBLE、または
SQL_INT64
short、long、double、また
は ISC_INT64
いいえ
NUMERIC
SQL_SHORT + 1、
SQL_LONG + 1、
SQL_DOUBLE + 1、または
SQL_INT64 + 1
short、long、double、また
は ISC_INT64
はい
SMALLINT
SQL_SHORT
short
いいえ
SMALLINT
SQL_SHORT + 1
short
はい
TIME
SQL_TIME
ISC_TIME
いいえ
TIME
SQL_TIME + 1
ISC_TIME
はい
TIMESTAMP
SQL_TIMESTAMP
ISC_TIMESTAMP
いいえ
TIMESTAMP
SQL_TIMESTAMP +1
ISC_TIMESTAMP
はい
VARCHAR
SQL_VARYING
最初の 2 バイトは 文字列の
長さを示す short。
いいえ
残りは char[ ]
VARCHAR
SQL_VARYING + 1
最初の 2 バイトは 文字列の
長さを示す short。
はい
残りは char[ ]
第 13 章 動的 SQL
13-13
XSQLDA
メモ
DECIMAL と NUMERIC のデータ型は、内部的には SMALLINT、INTEGER、
DOUBLE PRECISION、または 64 ビット整数のいずれかのデータ型として格納されま
す。整数型 sqltype の SMALLINT、INTEGER、64 ビット整数に精度が設定されている
場合、DECIMAL または NUMERIC のフィールドであれば sqlsubtype に情報が含められ
ます。
• sqlsubtype が 1 であれば NUMERIC です。
• sqlsubtype が 2 であれば DECIMAL です。
パラメータまたは選択リストのデータ型に関する情報は、XSQLVAR 構造体の sqltype
フィールドに格納されます。この場合、sqltype フィールドの値で示される情報には次の 2
つがあります。
• パラメータまたは選択リスト項目のデータ型
• sqlind フィールドが使用されているかどうかの情報。このフィールドが使用されている場
合、その値によりパラメータまたは取り出されるデータが NULL であるか(値は -1)
、ま
たは NULL でないか(値は 0)がわかります。
たとえば、XSQLVAR 構造体の sqltype フィールドが SQL_TEXT の場合、パラメータま
たは選択リストのデータ型は CHAR で、sqlind フィールドは使用されず、NULL の検査
は行われない(というより、理論的には NULL 値の格納が禁止されている)ことを表しま
す。一方、sqltype フィールドが SQL_TEXT + 1 の場合、パラメータまたは選択リストの
データ型は同じく CHAR で、sqlind フィールドが使用され、パラメータまたは選択リスト
が NULL であるかどうかの検査が可能であることを示します。
ヒント
C の場合は sqltype & 1 を使ってパラメータや選択リストに NULL が格納されている
かどうかを確認することができます。この sqltype & 1 による評価が 0 の場合、パラメー
タや選択リストには NULL は格納されていません。また、評価が 1 の場合、パラメータや
選択リストに NULL が格納されていることになります。次は、sqltype & 1 の使用例で
す。
if (sqltype & 1 == 0)
{
/* NULL を含まないパラメータまたは選択リスト項目 */
}
else
{
/* NULL を含むパラメータまたは選択リスト項目 */
}
PREPARE INTO と DESCRIBE では、デフォルトで type+1 のマクロ関数が返されます。
したがって、PREPARE INTO や DESCRIBE を使った場合は、必ず sqlind フィールドを
検査して値が NULL であるかどうかを確認する必要があります。
可変長文字列データ型の操作
VARCHAR、CHARACTER VARYING、NCHAR VARYING の各データ型は、DSQL
では取り扱いに注意が必要です。この種のデータ型では、データの最初の 2 バイトには文
字列の長さに関する情報が収められているためです。文字列の実際のバイトは、その残り
のデータに格納されています。
13-14 埋 め 込 み S Q L ガ イ ド
XSQLDA
アプリケーションでこのような可変長文字列の抽出と処理のコーディングを避けるには、
SQL マクロ式を使って固定長のデータ型に強制的に変換することができます。可変長デー
タを強制的に固定長に変換する方法については、13-15 ページの「データ型の強制変換」
を参照してください。
アプリケーションで可変長データを直接処理することもできます。それには、まず文字列の
最初の 2 バイトを取り出して文字列自体の長さを判定し、次に文字列をバイト単位で読み
取って NULL 終端のバッファに格納します。
NUMERIC と DECIMAL のデータ型の扱い
SQL の DECIMAL と NUMERIC の 2 つのデータ型は、InterBase の内部では
SMALLINT、INTEGER、DOUBLE PRECISION、または 64 ビット整数のいずれかの
データ型で格納されます。どのデータ型が採用されるかは、列の定義で使用されている
DECIMAL ま た は NUMERIC の有効桁数と小数点以下の桁数によって決まります。
DECIMAL または NUMERIC の値が、実際に 3 つのうちのどのデータ型でデータベース
に格納されているかは、isql を使ってテーブルの列の定義を確認します。たとえば、定義が
NUMERIC であれば、実際のデータは 64 ビット整数で格納されていることになります。
DECIMAL ま た は NUMERIC のデータが、InterBase の内部で SMALLINT または
INTEGER で格納されている場合、その値は整数の形で格納されています。この場合、
DSQL でデータの取り出しを行うと、XSQLVAR の sqlscale フィールドはマイナスの数値
に設定されます。このマイナスの数値は 10 分の 1 を表す係数で、格納されている整数
(sqldata で返されます)をこの係数で割ることによって、NUMERIC または DECIMAL
の値が小数点以下の桁数付きで正しく変換されます。たとえば、sqlscale が –1 の場合、整
数を 10 で割ることで、小数点以下の桁数付きの数値に変換されます。また、–2 では 100、–
3 では 1000 で割ることで、正しく変換されます。
データ型の強制変換
DSQL で入力パラメータまたは選択リストを処理する場合、データ型の変換が必要なこと
があります。この変換処理をデータ型の強制変換と呼んでいます。たとえば、パラメータま
たは取り出されるデータのデータ型が VARCHAR の場合、データ型の強制変換が必要に
なります。VARCHAR のデータは、最初の 2 バイトに文字列の長さを示す情報が格納され
ています。その残りが実際の文字列となります。このため、VARCHAR(SQL_VARYING)
を CHAR(SQL_TEXT)に変換すると、データ処理が簡単になります。
データ型の強制変換を行う場合、変換するデータ型に互換性があることが必要です。たとえ
ば、SQL_VARYING から SQL_TEXT へ、SQL_SHORT から SQL_LONG への変換など
は互換性があるため問題ありません。
文字データ型の強制変換
VARCHAR(SQL_VARYING)を CHAR(SQL_TEXT)に変換する場合、パラメータ
または選択リスト項目の XSQLVAR 構造体の sqltype フィールドを変換先の SQL マクロ
定数(SQL_TEXT)に変更します。次の文の var は XSQLVAR 構造体を指すポインタで、
こ の XSQLVAR の sqltype フィールドは SQL_VARYING です。このフィールドを
SQL_TEXT データ型に変換しています。
第 13 章 動的 SQL
13-15
XSQLDA
var->sqltype = SQL_TEXT;
なお、文字データ型を変換したときは、データの格納に必要なメモリ領域を確保しなけれ
ばなりません。この場合、XSQLVAR の sqllen フィールドには変換元のデータサイズに関
する情報が収められています。これを参考にして、XSQLVAR の sqldata フィールドを
データのアドレスに設定してください。
数値データ型の強制変換
数 値デ ー タ型 を 他 のデ ー タ型に変換する場合、パラメータまたは選択リスト項目の
XSQLVAR 構造体の sqltype フィールドを変換先の SQL マクロ定数に変更します。次の
文の var は、XSQLVAR 構造体を指すポインタで、この XSQLVAR の sqltype フィール
ドは SQL_SHORT データ型です。この項目を SQL_LONG データ型に変換しています。
var->sqltype = SQL_LONG;
重要
桁数の大きいデータ型を桁数の小さいデータ型に強制変換してはなりません。 データが失
われる原因となります。
NULL インジケータの設定
パラメータまたは取り出されるデータの値が NULL のこともあります。この場合、sqlind
フィールドを使用して、引数または取り出されるデータの NULL ステータス(値が NULL
かどうか)を調べることができます。なお、sqlind フィールドの値を格納するには、メモリ
領域の確保が必要です。
入力 XSQLDA では、値が NULL の場合、sqlind フィールドを –1 に設定します。NULL
でない場合、sqlind フィールドを 0 に設定します。
出力 XSQLDA では、sqlind フィールドが –1 の場合、データは NULL であることを示し
ます。それ以外のときは、データは NULL でないことを示します。
数値データの配置(アラインメント)
一般に、データ型が数値の変数は、コンパイラによりメモリ境界に規則的に並ぶように割
り付けられます(アラインメントされる)。これに対して、XSQLDA 構造体や XSQLVAR
構造体で見られるように、動的に割り当てられたバッファに数値データが格納される場合
は、バッファ中に規則正しくアラインメントされるように注意する必要があります。
RISC プロセッサの場合など、プラットフォームによっては、動的に割り当てられた構造体
の数値データがメモリの中で必ず正しくアラインメントされるように注意しなければなり
ません。アラインメントは、データのデータ型とプラットフォームによって異なります。
たとえば、Sun SPARCstation では、16 ビット整数は 2 で割れるアドレスに格納しなけれ
ばなりません。また、32 ビットの場合、4 で割れるアドレスに格納することが必要です。
このように、通常、システムにより所定のアラインメントの値が決まっており、このアラ
インメントの値で先頭のバイトのアドレスが割れる場合、データは正しくアラインメント
していることになります。アラインメントの要件はシステムやコンパイラによって異なる
ことがあるため、付属のマニュアルを参照してください。
13-16 埋 め 込 み S Q L ガ イ ド
DSQL のプログラミング手法
なお、数値データのアラインメントについては便利な一般法則があります。これは、デー
タ型のサイズが、必ずそのデータ型のアラインメントの値であるというものです。式を使っ
て表すと、データ型を T とした場合、(T) のサイズが n に等しければ、n で割り切れるア
ドレスが T のアラインメントのためのアドレスということになります。したがって、次の
マクロの式を使用すると、データのアラインメントが可能になります。
#define ALIGN(ptr, n) ((ptr + n - 1) & ~(n - 1))
上の例で、ptr は char のポインタです。
次のコードは、ALIGN マクロの使用例です。
char *buffer_pointer, *next_aligned;
next_aligned = ALIGN(buffer_pointer, sizeof(T));
DSQL のプログラミング手法
文字列としての SQL 文を処理する場合の DSQL プログラミング手法には、全部で 4 つの
種類があります。どの DSQL プログラミング手法が適切かは、SQL 文の内容によって異な
ります。また、SQL 文にプレースホルダ(入力パラメータ)があるかどうかも、選択の要
因となります。次の表は、使用すべき DSQL プログラミング手法を示したものです。
表 13.4 SQL 文の文字列と推奨される処理方法
クエリーか ?
プレースホルダがあるか ?
使用するプログラミング手法
いいえ
いいえ
手法 1
いいえ
はい
手法 2
はい
いいえ
手法 3
はい
はい
手法 4
手法 1: パラメータのない非クエリー SQL 文
SQL 文がクエリーではなく、プレースホルダパラメータもない場合、次の 2 とおりの処理
方法があります。
• EXECUTE IMMEDIATE を使って準備と実行を一括して行います。通常、SQL 文の実行
が一度限りのときに使用します。
• PREPARE を使って SQL 文の分析(用意)を行うとともに SQL 文に名前を付け、その後、
EXECUTE で SQL 文を実行します。通常、その SQL 文をアプリケーションで何回も実行
するときに使用します。
EXECUTE IMMEDIATE による実行
1 EXECUTE IMMEDIATE を使って SQL 文を実行する場合、手順は次のようになりま
す。EXECUTE IMMEDIATE は、実行が一度限りの場合に使用します。
2
ユーザーからの SQL 文の入力を求めます。または、処理する SQL 文の文字列を作成
します。以下の文は、SQL 文の文字列を作成します。
第 13 章 動的 SQL
13-17
DSQL のプログラミング手法
char *str = "UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05";
3 EXECUTE IMMEDIATE で SQL 文の文字列を分析し、実行します。
EXEC SQL
EXECUTE IMMEDIATE :str;
メモ
EXECUTE IMMEDIATE では変数のかわりに文字列定数も使用できます。次に例を示し
ます。
EXEC SQL
EXECUTE IMMEDIATE
'UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05';
PREPARE と EXECUTE による実行
PREPARE と EXECUTE を使って SQL 文の文字列を何度か実行する場合、手順は次のよ
うになります。
1
ユーザーからの SQL 文の入力を求めます。または、処理する SQL 文の文字列を作成
します。以下の文は、SQL 文の文字列を作成します。
char *str = "UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05";
2 PREPARE を使って SQL 文の文字列を分析し、SQL 文に名前を付けます。この名前
は、後続の EXECUTE で使用します。
EXEC SQL
PREPARE SQL_STMT FROM :str;
上の例で、SQL_STMT は分析対象の SQL 文の文字列に割り当てられる名前です。
3 EXECUTE を使って名前を付けた SQL 文を実行します。次の文は、SQL_STMT とい
う名前の SQL 文を実行する例です。
EXEC SQL
EXECUTE SQL_STMT;
メモ
PREPARE では変数のかわりに文字列定数も使用できます。次に例を示します。
EXEC SQL
PREPARE SQL_STMT FROM
'UPDATE DEPARTMENT SET BUDGET = BUDGET * 1.05';
作成したステートメント文字列は、アプリケーション内で必要に応じて何度も使用でき
ます。
手法 2: パラメータのある非クエリー SQL 文
SQL 文がクエリーではなく、プレースホルダパラメータがある場合、次の 2 段階の手順で
処理を行います。
1 SQL 文のパラメータを処理するための入力 XSQLDA を作成します。
2 パラメータのある SQL 文の文字列を用意し、実行します。
13-18 埋 め 込 み S Q L ガ イ ド
DSQL のプログラミング手法
入力 XSQLDA の作成
SQL 文にプレースホルダパラメータがある場合、そのプレースホルダが実際のデータに置
き換えられた後、SQL 文の文字列が実行されるという流れで処理が行われます。この場合、
パラメータのある SQL 文の文字列を作成したときは、パラメータの値は不定です。このた
め、入力 XSQLDA を作成し、この入力 XSQLDA を介して SQL 文 SQL 文 SQL 文の実
行時にパラメータの値を渡すという処理が必要になります。入力 XSQLDA の作成は、次
の手順で行います。
1
入力 XSQLDA を格納するための変数を宣言します。この入力 XSQLDA によってパ
ラメータが処理されます。たとえば、in_sqlda という名前の XSQLDA の変数を宣言す
る場合は、次のようになります。
XSQLDA *in_sqlda;
2
宣言した XSQLDA の XSQLVAR 構造体にアクセスするための変数を宣言します。
XSQLVAR *var;
なお、この変数(ポインタ)の宣言はオプションで必ずしも必要ではありませんが、こ
の変数を宣言することで、後続の文での XSQLVAR 構造体の参照が簡単になります。
3 XSQLDA_LENGTH マクロを使って XSQLDA を格納するためのメモリを割り当てま
す。たとえば、in_sqlda のメモリを割り当てる場合、記述は次のようになります。
in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));
上の文では、XSQLVAR 10 個分の領域を割り当てています。つまり、in_sqlda には、
10 個のパラメータを格納するだけの領域が与えられたことになります。
4 XSQLDA の version フィールドに SQLDA_CURRENT_VERSION を設定し、sqln
フィールドに XSQLVAR 構造体の割り当て数を設定します。
in_sqlda_version = SQLDA_CURRENT_VERSION;
in_sqlda->sqln = 10;
パラメータのある SQL 文の文字列の作成と実行
以上の要領でパラメータを格納する準備ができたら、処理対象となる SQL 文の文字列を作
成します。また、ローカル変数を使用して、文字列中のプレースホルダパラメータをそれぞ
れ、XSQLVAR 構造体の sqldata フィールドに割り当てます。
パラメータのある非クエリーの SQL 文の文字列の作成と実行は、次の手順で行います。
1
ユーザーからの SQL 文の文字列の入力を求めます。または、処理する SQL 文の文字
列を作成します。たとえば、次の文は、プレースホルダパラメータがある SQL 文の文
字列を作成する例です。
char *str = "UPDATE DEPARTMENT SET BUDGET = ?, LOCATION = ?";
上の SQL 文にはパラメータが 2 つあります。これらは、それぞれ BUDGET 列の値と
LOCATION 列の値に相当します。
2 PREPARE を使って SQL 文の文字列を分析し、SQL 文に名前を付けます。この名前
は、後続の DESCRIBE および EXECUTE で使用します。
EXEC SQL
PREPARE SQL_STMT FROM :str;
SQL_STMT は、分析対象の SQL 文の文字列に割り当てられる名前です。
第 13 章 動的 SQL
13-19
DSQL のプログラミング手法
3 DESCRIBE INPUT を使用して、入力 XSQLDA に SQL 文中のパラメータに関する情
報を入力します。
EXEC SQL
DESCRIBE INPUT SQL_STMT USING SQL DESCRIPTOR in_sqlda;
4 XSQLDA の sqln フィールドの値と XSQLDA の sqld フィールドの値を比較し、パラ
メータを全部格納できるだけの数の XSQLVAR が割り当てられているかどうかを検査
します。この場合、sqln フィールドの値は、sqld フィールドの値と同じか大きくなけ
ればなりません。sqln フィールドの値が sqld フィールドの値より小さいときは、先に
XSQLDA に割り当てた領域をいったん解放し、パラメータの数(sqld フィールドで示
されます)に見合うだけの領域を再度割り当てます。また、sqln と version を再設定
し、もう一度 DESCRIBE INPUT を実行します。この処理の記述は次のようになりま
す。
if (in_sqlda->sqld > in_sqlda->sqln)
{
n = in_sqlda->sqld;
free(in_sqlda);
in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));
in_sqlda->sqln = n;
in_sqlda->version = SQLDA_CURRENT_VERSION;
EXEC SQL
DESCRIBE INPUT SQL_STMT USING SQL DESCRIPTOR in_sqlda;
}
5 XSQLDA 内の各 XSQLVAR パラメータ構造体を処理します。各パラメータ構造体の
処理は、次の 4 段階に分かれます。
• パラメータのデータ型を強制変換する(オプション)。
• データ(XSQLVAR の sqldata フィールドによって示される)を格納するローカル
領域を確保する。この処理は、実行時にローカル変数の領域の割り当てが行われて
いない場合に限り必要です。次に、ローカル変数の格納領域を動的に割り当てる例
があるため、そちらを参考にしてください。
• パラメータに値を設定する。値は、パラメータに定義されているデータ型で設定し
なければなりません。この処理は必須です。
• パラメータに NULL 値インジケータを設定する。
以上の処理を記述すると、次のようなコードになります。ここでは、XSQLDA である
in_sqlda の中の各 XSQLVAR 構造体をループで処理しています。
for (i=0, var = in_sqlda->sqlvar; i < in_sqlda->sqld; i++, var++)
{
/* ここで、XSQLVAR パラメータ構造体を処理する
パラメータ構造体は、var で示す */
dtype = (var->sqltype & ~1) /* ここで、NULL フラグを削除します */
switch(dtype)
{
case SQL_VARYING:/* SQL_TEXT に強制変換します */
var->sqltype = SQL_TEXT;
13-20 埋 め 込 み S Q L ガ イ ド
DSQL のプログラミング手法
/* ローカル変数の格納領域を割り当てます */
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
. . .
break;
case SQL_TEXT:
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
/* パラメータに値を設定します */
. . .
break;
case SQL_LONG:
var->sqldata = (char *)malloc(sizeof(long));
/* パラメータに値を設定します */
*(long *)(var->sqldata) = 17;
break;
. . .
} /* switch 文の終わり */
if (var->sqltype & 1)
{
/* NULL ステータスを保持する変数を割り当てる */
var->sqlind = (short *)malloc(sizeof(short));
}
}
/* for ループの終わり */
データ型の強制変換と NULL インジケータについては、13-15 ページの「データ型の
強制変換」を参照してください。
6 EXECUTE を使って名前を付けた SQL 文を実行します。入力 XSQLDA の中のパラ
メータ(XSQLVAR 構造体)は、USING SQL DESCRIPTOR 句を使って参照します。
次は、SQL_STMT という名前の SQL 文の文字列を実行する場合の文です。
EXEC SQL
EXECUTE SQL_STMT USING SQL DESCRIPTOR in_sqlda;
SQL 文の文字列の再実行
パラメータ付きの非クエリー 文の文字列を作成したら、その SQL 文をアプリケーション
で繰り返し使用できます。この場合、パラメータ(入力 XSQLDA)に新しいデータを入れ、
NULL インジケータを設定します。
パラメータへの値の入力と NULL インジケータの設定は、この章の「パラメータのある
SQL 文の文字列の作成と実行」の 3 ~ 5 の手順と同じです。
手法 3: パラメータのないクエリー SQL 文
SQL 文の文字列がクエリーでパラメータがない場合、次の 3 段階の手順で処理を行いま
す。
1
出力 XSQLDA を作成します。この出力 XSQLDA によって、クエリーの実行時に取
り出される選択リスト項目を処理します。
第 13 章 動的 SQL
13-21
DSQL のプログラミング手法
2 SQL 文の文字列(クエリー)を用意します。
3 カーソルを使って SQL 文を実行するとともに、出力 XSQLDA から選択リストを取り
出します。
出力 XSQLDA の作成
クエリーでは、通常、1 つ以上の行が返されます。この行に含まれる列のデータを選択リス
トと呼んでいます(記述によってはデータが複数のこともある)。ただし、SQL 文の文字列
の作成時には、返されるデータの数は不定なため、出力 XSQLDA を作成し、実行時、こ
の出力 XSQLDA にいったん選択リスト項目を格納するという処理が必要になります。出
力 XSQLDA の作成は、次の手順で行います。
1
出力 XSQLDA を格納するための変数を宣言する。これは、取得する行の各列にある
データを保存するために必要です。次の宣言は、out_sqlda という XSQLDA を作成し
ます。
XSQLDA *out_sqlda;
2
宣言した XSQLDA の XSQLVAR 構造体にアクセスするための変数を宣言します。
XSQLVAR *var;
なお、この変数(ポインタ)の宣言はオプションで必ずしも必要ではありませんが、こ
の変数を宣言することで、後続の文での XSQLVAR 構造体の参照が簡単になります。
3 XSQLDA_LENGTH マクロを使って XSQLDA を格納するためのメモリを割り当てま
す。たとえば、out_sqlda のメモリを割り当てる場合、記述は次のようになります。
out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));
上の文では、XSQLVAR 構造体 10 個分の領域を割り当てています。つまり、
out_sqlda には、10 個の選択リストを格納するだけの領域が与えられます。
4 XSQLDA の version フィールドに SQLDA_CURRENT_VERSION を設定します。ま
た、sqln フィールドを、割り当てられた XSQLVAR 構造体の数に設定します。
out_sqlda->version = SQLDA_CURRENT_VERSION;
out_sqlda->sqln = 10;
クエリー SQL 文の文字列の用意
以上の要領でデータを格納する XSQLDA の作成ができたら、クエリー SQL 文の文字列を
作成し、SQL 文の文字列の実行に備えます。実行すると、InterBase によりクエリーの記述
にしたがって行が取り出され、選択リスト項目が作成されます。
クエリーの SQL 文の文字列は、次のように作成します。
1
ユーザーに SQL 文の文字列の入力を求めます。または、クエリー SQL 文の文字列を
作成します。次の文は、クエリー SQL 文の文字列を作成する例です。
char *str = "SELECT * FROM CUSTOMER";
この文では、SELECT の右にアスタリスク(*)を記述しています。このアスタリスク
は、テーブルのすべての列を表すワイルドカード記号です。したがって、このクエリー
では、テーブルのすべての列と同じ数のデータが返されます。
13-22 埋 め 込 み S Q L ガ イ ド
DSQL のプログラミング手法
2 PREPARE を使って SQL 文の文字列を分析するとともに SQL 文に名前を付けます。こ
の名前は、後続の DESCRIBE や EXECUTE などの文で使用します。
EXEC SQL
PREPARE SQL_STMT FROM :str;
SQL_STMT は、分析対象の SQL 文の文字列に割り当てられる名前です。
3 DESCRIBE OUTPUT を使用して、SQL 文から返されるデータの情報を出力
XSQLDA に渡します。
EXEC SQL
DESCRIBE OUTPUT SQL_STMT INTO SQL DESCRIPTOR out_sqlda;
4 XSQLDA の sqln フィールドの値と XSQLDA の sqld フィールドの値を比較し、クエ
リーで返される選択リストを全部格納できるだけの数の XSQLVAR が割り当てられて
いるかどうかを検査します。ここで、sqln フィールドの値が sqld フィールドの値より
小さいときは、先に XSQLDA に割り当てた領域をいったん解放し、選択リストの数
(sqld フィールドで示されます)に見合うだけの領域を再度割り当てます。また、sqln
と version を再設定し、もう一度 DESCRIBE OUTPUT を実行します。この処理の記
述は次のようになります。
if (out_sqlda->sqld > out_sqlda->sqln)
{
n = out_sqlda->sqld;
free(out_sqlda);
out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));
out_sqlda->sqln = n;
out_sqlda->version = SQLDA_CURRENT_VERSION;
EXEC SQL
DESCRIBE OUTPUT SQL_STMT INTO SQL DESCRIPTOR out_sqlda;
}
5
データを表す XSQLVAR 構造体をそれぞれ処理します。次の手順にしたがいます。
• データのデータ型を強制変換する(オプション)。
• データ(XSQLVAR の sqldata フィールドによって示される)を格納するローカル
領域を確保する。この処理は、実行時にローカル変数の領域の割り当てが行われて
いない場合に限り必要です。次に、ローカル変数の格納領域を動的に割り当てる例
があるため、そちらを参考にしてください。
• パラメータに NULL 値インジケータを設定する。
以上の処理を記述するコードは次のとおりです。ここでは、XSQLDA 構造体
out_sqlda の中の各 XSQLVAR 構造体をループで処理しています。
for (i=0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++)
{
dtype = (var->sqltype & ~1) /* ここで、フラグビットを削除します */
switch (dtype)
{
case SQL_VARYING:
var->sqltype = SQL_TEXT;
第 13 章 動的 SQL
13-23
DSQL のプログラミング手法
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2);
break;
case SQL_TEXT:
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
break;
case SQL_LONG:
var->sqldata = (char *)malloc(sizeof(long));
break;
. . .
/* 残りの型を処理します */
} /* switch 文の終わり */
if (sqltype & 1)
{
/* NULL ステータスを保持する変数を割り当てます */
var->sqlind = (short *)malloc(sizeof(short));
}
}
/* for ループの終わり */
データ型の強制変換と NULL インジケータについては、13-15 ページの「データ型の
強制変換」を参照してください。
SQL 文の文字列のカーソルによる実行
クエリー SQL 文の文字列の準備ができたら、カーソルを使って SQL 文を実行し選択リス
トを取り出します。なお、InterBase でのカーソル宣言はすべてコンパイルの時点で固定さ
れるとともに、埋め込んだ文もアプリケーションに挿入されます。したがって、DSQL ア
プリケーションを作成する場合は、事前に使用されると思われるカーソルを想定し宣言し
ておく必要があります。
カーソルを使って SQL 文を実行する場合は、ループ構造を使用します。この場合、まず 1
行だけ取り出し、その行について選択リスト項目(列のデータ)を処理します。その後、同
様に次の行の取り出しと選択リストの処理を行います。
カーソルによる SQL 文の実行と行の取り出しと選択リスト項目の処理は、次の手順で行います。
1 SQL 文の文字列の実行に必要なカーソルを宣言します。たとえば、次は、SQL_STMT
という名前の SQL 文の文字列で使用するカーソルとして DYN_CURSOR を宣言して
いる例です。
EXEC SQL
DECLARE DYN_CURSOR CURSOR FOR SQL_STMT;
2
宣言したカーソルを開きます。
EXEC SQL
OPEN DYN_CURSOR;
SQL 文の文字列は、カーソルを開くと同時に実行されるとともに、行のアクティブ
セットが作成されます。カーソルとアクティブセットの詳細は、第 6 章「データ処理」
を参照してください。
13-24 埋 め 込 み S Q L ガ イ ド
DSQL のプログラミング手法
3
ループを使って行を 1 行ずつ取り出し、各行について選択リスト(列のデータ)を処
理します。たとえば、次は、DYN_CURSOR カーソルを使って行を 1 行ずつ取り出す
とともに、各行について選択リスト項目の処理を行っているループの例です。ここで
は、アプリケーション独自の関数(process_column())を使って行の列のデータを処理
しています。
while (SQLCODE == 0)
{
EXEC SQL
FETCH DYN_CURSOR USING SQL DESCRIPTOR out_sqlda;
if (SQLCODE == 100)
break;
for (i = 0; i < out_sqlda->sqld; i++)
{
process_column(out_sqlda->sqlvar[i]);
}
}
上の例の process_column() 関数は、返された各選択リスト項目を処理します。次の概
略コードは、このような関数の設定方法を示します。
void process_column(XSQLVAR *var)
{
/* NULL 値かどうかをテストします */
if ((var->sqltype & 1) && (*(var->sqlind) = -1))
{
/* NULL 値を処理します */
}
else
{
/* データを処理します */
}
. . .
}
4
全部の行の取り出しが完了したら、カーソルを閉じます。
EXEC SQL
CLOSE DYN_CURSOR;
クエリー SQL 文の文字列の再実行
パラメータのないクエリー 文の文字列を作成したら、それをアプリケーションで繰り返し
使用できます。この場合、カーソルを閉じてから、もう一度開きます。
カーソルを再度開いて選択リスト項目を処理するには、この章の「クエリー SQL 文の文字
列のカーソルによる実行」のステップ 2 ~ 4 を繰り返してください。
第 13 章 動的 SQL
13-25
DSQL のプログラミング手法
手法 4: パラメータのあるクエリー SQL 文
SQL 文がクエリーでプレースホルダパラメータがある場合、SQL クエリー 文の文字列の
処理は次の手順で行います。
1
入力 XSQLDA を作成します。この入力 XSQLDA は、SQL 文の文字列のパラメータ
を処理するために使用します。
2
出力 XSQLDA を作成します。この出力 XSQLDA によって、クエリーの実行時に取
り出される選択リスト項目を処理します。
3
4
パラメータのあるクエリー SQL 文の文字列を用意します。
カーソルを使用して、入力 XSQLDA に格納されているパラメータの値をもとに SQL
文を実行するとともに、出力 XSQLDA から選択リスト項目を取り出します。
入力 XSQLDA の作成
SQL 文にプレースホルダパラメータがある場合、そのプレースホルダが実際のデータに置
き換えられた後、SQL 文の文字列が実行されるという流れで処理が行われます。この場合、
パラメータのある SQL 文の文字列を作成したときは、パラメータの値は不定です。このた
め、入力 XSQLDA を作成し、この入力 XSQLDA を介して SQL 文の実行時にパラメータ
の値を渡すという処理が必要になります。入力 XSQLDA の作成は、次の手順で行います。
1
入力 XSQLDA を格納するための変数を宣言します。この入力 XSQLDA によってパ
ラメータが処理されます。たとえば、in_sqlda という名前の XSQLDA の変数を宣言す
る場合は、次のようになります。
XSQLDA *in_sqlda;
2
宣言した XSQLDA の XSQLVAR 構造体にアクセスするための変数を宣言します。
XSQLVAR *var;
なお、この変数(ポインタ)の宣言はオプションで必ずしも必要ではありませんが、こ
の変数を宣言することで、後続の文での XSQLVAR 構造体の参照が簡単になります。
3 XSQLDA_LENGTH マクロを使って XSQLDA を格納するためのメモリを割り当てま
す。たとえば、in_slqda のメモリを割り当てる場合、記述は次のようになります。
in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));
上の文では、XSQLVAR 構造体 10 個分の領域を割り当てています。つまり、in_sqlda
には、10 個の入力パラメータを格納するだけの領域が与えられることになります。構
造体が割り当てられると XSQLVAR ごとに sqldata フィールドに値が設定されます。
4 XSQLDA の version フィールドに SQLDA_CURRENT_VERSION を設定します。ま
た、sqln フィールドを、割り当てられた XSQLVAR 構造体の数に設定します。
in_sqlda->version = SQLDA_CURRENT_VERSION;
in_sqlda->sqln = 10;
13-26 埋 め 込 み S Q L ガ イ ド
DSQL のプログラミング手法
出力 XSQLDA の作成
SQL 文の文字列を作成するときは、どのくらいの数のデータが返されるかは事前に判定す
ることはできません。このため、出力 XSQLDA を作成し、実行時、この出力 XSQLDA
にいったんデータを格納するという処理が必要になります。出力 XSQLDA の作成は、次
の手順で行います。
1
入力 XSQLDA を格納するための変数を宣言します。この入力 XSQLDA によってパ
ラメータが処理されます。たとえば、out_sqlda という名前の XSQLDA の変数を宣言
する場合は、次のようになります。
XSQLDA *out_sqlda;
2
宣言した XSQLDA の XSQLVAR 構造体にアクセスするための変数を宣言します。
XSQLVAR *var;
なお、この変数(ポインタ)の宣言はオプションで必ずしも必要ではありませんが、こ
の変数を宣言することで、後続の文での XSQLVAR 構造体の参照が簡単になります。
3 XSQLDA_LENGTH マクロを使って XSQLDA を格納するためのメモリを割り当てま
す。たとえば、out_sqlda のメモリを割り当てる場合、記述は次のようになります。
out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(10));
上の文では、XSQLVAR 構造体 10 個分の領域を割り当てています。つまり、
out_sqlda には、10 個の選択リストを格納するだけの領域が与えられます。
4 XSQLDA の version フィールドに SQLDA_CURRENT_VERSION を設定します。ま
た、sqln フィールドを、割り当てられた XSQLVAR 構造体の数に設定します。
out_sqlda->version = SQLDA_CURRENT_VERSION;
out_sqlda->sqln = 10;
パラメータを持つクエリー SQL 文の文字列の用意
SQL 文の文字列のパラメータを格納する入力 XSQLDA と、文を実行したときに返される
選択リスト項目を格納する出力 XSQLDA が作成できたら、その SQL 文の文字列を実行す
ることができます。SQL 文の文字列の準備が整うと、InterBase は SQL 文の文字列内のプ
レースホルダパラメータを、使用される実パラメータに関する情報に置き換えます。パラ
メータに関する情報を入力 XSQLDA に割り当てないと(多くの場合は調整も行わない
と)、SQL 文は実行できません。SQL 文の文字列を実行すると、InterBase は出力 XSQLDA
に選択リスト項目を格納します。
パラメータのあるクエリー SQL 文の文字列は、次の手順で作成します。
1
ユーザーからの SQL 文の文字列の入力を求めます。または、処理する SQL 文の文字
列を作成します。たとえば、次の文は、プレースホルダパラメータがある SQL 文の文
字列を作成する例です。
char *str = "SELECT * FROM DEPARTMENT WHERE BUDGET = ?, LOCATION =
?";
上の SQL 文にはパラメータが 2 つあります。これらは、それぞれ BUDGET 列の値と
LOCATION 列の値に相当します。
2 PREPARE を使って SQL 文の文字列を準備し、SQL 文に名前を付けます。この名前
は、後続の DESCRIBE および EXECUTE で使用します。
第 13 章 動的 SQL
13-27
DSQL のプログラミング手法
EXEC SQL
PREPARE SQL_STMT FROM :str;
SQL_STMT は、分析対象の SQL 文の文字列に割り当てられる名前です。
3 DESCRIBE INPUT を使用して、入力 XSQLDA に SQL 文中のパラメータに関する情
報を入力します。
EXEC SQL
DESCRIBE INPUT SQL_STMT USING SQL DESCRIPTOR in_sqlda;
4 XSQLDA の sqln フィールドの値と XSQLDA の sqld フィールドの値を比較し、パラ
メータをすべて格納できるだけの数の XSQLVAR が割り当てられているかどうかを検
査します。sqln フィールドの値が sqld フィールドの値より小さいときは、先に
XSQLDA に割り当てた領域をいったん解放し、パラメータの数(sqld フィールドで示
されます)に見合うだけの領域を再度割り当てます。また、sqln と version を再設定
し、もう一度 DESCRIBE INPUT を実行します。この処理の記述は次のようになりま
す。
if (in_sqlda->sqld > in_sqlda->sqln)
{
n = in_sqlda->sqld;
free(in_sqlda);
in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));
in_sqlda->sqln = n;
in_sqlda->version = SQLDA_CURRENT_VERSION;
EXEC SQL
DESCRIBE INPUT SQL_STMT USING SQL DESCRIPTOR in_sqlda;
}
5
入力 XSQLDA 内の各 XSQLVAR パラメータ構造体を処理します。各パラメータ構造
体の処理は、次の 4 段階に分かれます。
• パラメータのデータ型を強制変換する(オプション)。
• データ(XSQLVAR の sqldata フィールドによって示される)を格納するローカル
領域を確保する。この処理は、実行時にローカル変数の領域の割り当てが行われて
いない場合に限り必要です。次に、ローカル変数の格納領域を動的に割り当てる例
があるため、そちらを参考にしてください。
• パラメータに値を設定する。値は、パラメータに定義されているデータ型で設定し
なければなりません。この処理は必須です。
• パラメータ用の NULL 値インジケータを用意する。
これらの処理は、上記の順序で行わなければなりません。次のコードは、in_sqlda の
中の XSQLVAR 構造体(パラメータに対応)ごとにループして、これらの処理を行っ
ています。
for (i=0, var = in_sqlda->sqlvar; i < in_sqlda->sqld; i++, var++)
{
/* ここで、XSQLVAR パラメータ構造体を処理する
パラメータ構造体は、var で示す */
dtype = (var->sqltype & ~1) /* ここで、フラグビットを削除します */
13-28 埋 め 込 み S Q L ガ イ ド
DSQL のプログラミング手法
switch (dtype)
{
case SQL_VARYING:/* SQL_TEXT に強制変換します */
var->sqltype = SQL_TEXT;
/* 適切な格納領域を割り当てます */
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
/* パラメータに値を設定します。 SQL_LONG のケースを参照 */
. . .
break;
case SQL_TEXT:
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
/* パラメータに値を設定します。 SQL_LONG のケースを参照 */
. . .
break;
case SQL_LONG:
var->sqldata = (char *)malloc(sizeof(long));
/* パラメータに値を設定します */
*(long *)(var->sqldata) = 17;
break;
. . .
} /* switch 文の終わり */
if (sqltype & 1)
{
/* NULL ステータスを保持する変数を割り当てます */
var->sqlind = (short *)malloc(sizeof(short));
}
}
/* for ループの終わり */
データ型の強制変換と NULL インジケータについては、13-15 ページの「データ型の
強制変換」を参照してください。
6 DESCRIBE OUTPUT を使用して、SQL 文から返されるデータの情報を出力
XSQLDA に渡します。
EXEC SQL
DESCRIBE OUTPUT SQL_STMT INTO SQL DESCRIPTOR out_sqlda;
7 XSQLDA の sqln フィールドの値と XSQLDA の sqld フィールドの値を比較し、クエ
リーで返される選択リストを全部格納できるだけの数の XSQLVAR が割り当てられてい
るかどうかを検査します。ここで、sqln フィールドの値が sqld フィールドの値より小さ
いときは、先に XSQLDA に割り当てた領域をいったん解放し、選択リストの数(sqld
フィールドで示されます)に見合うだけの領域を再度割り当てます。また、sqln と
version を再設定し、もう一度 DESCRIBE OUTPUT を実行します。この処理の記述は
次のようになります。
if (out_sqlda->sqld > out_sqlda->sqln)
{
n = out_sqlda->sqld;
free(out_sqlda);
第 13 章 動的 SQL
13-29
DSQL のプログラミング手法
out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n));
out_sqlda->sqln = n;
out_sqlda->version = SQLDA_CURRENT_VERSION;
EXEC SQL
DESCRIBE OUTPUT SQL_STMT INTO SQL DESCRIPTOR out_sqlda;
}
8
データを表す XSQLVAR 構造体をそれぞれ処理します。次の手順にしたがいます。
• データのデータ型を強制変換する(オプション)。
• データ(XSQLVAR の sqldata フィールドによって示される)を格納するローカル
領域を確保する。この処理は、実行時にローカル変数の領域の割り当てが行われて
いない場合に限り必要です。次に、ローカル変数の格納領域を動的に割り当てる例
があるため、そちらを参考にしてください。
• データ用の NULL 値インジケータを用意します(オプション)。
以上の処理を記述するコードは次のとおりです。ここでは、XSQLDA 構造体
out_sqlda の中の各 XSQLVAR 構造体をループで処理しています。
for (i=0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++)
{
dtype = (var->sqltype & ~1) /* ここで、フラグビットを削除します */
switch (dtype)
{
case SQL_VARYING:
var->sqltype = SQL_TEXT;
break;
case SQL_TEXT:
var->sqldata = (char *)malloc(sizeof(char)*var->sqllen);
break;
case SQL_LONG:
var->sqldata = (char *)malloc(sizeof(long));
break;
/* 残りの型を処理します */
} /* switch 文の終わり */
if (sqltype & 1)
{
/* NULL ステータスを保持する変数を割り当てます */
var->sqlind = (short *)malloc(sizeof(short));
}
}
/* for ループの終わり */
データ型の強制変換と NULL インジケータについては、13-15 ページの「データ型の
強制変換」を参照してください。
13-30 埋 め 込 み S Q L ガ イ ド
DSQL のプログラミング手法
クエリー SQL 文の文字列のカーソルによる実行
クエリー SQL 文の文字列の準備ができたら、カーソルを使って SQL 文を実行し選択リス
トを取り出します。なお、InterBase でのカーソル宣言はすべてコンパイルの時点で固定さ
れるとともに、埋め込んだ文もアプリケーションに挿入されます。したがって、DSQL ア
プリケーションを作成する場合は、事前に使用されると思われるカーソルを想定し宣言し
ておく必要があります。
カーソルを使って SQL 文を実行する場合は、ループ構造を使用します。この場合、まず 1
行だけ取り出し、その行について選択リスト項目(列のデータ)を処理します。その後、同
様に次の行の取り出しと選択リストの処理を行います。
カーソルによる SQL 文の実行と行の取り出しと選択リスト項目の処理は、次の手順で行い
ます。
1 SQL 文の文字列の実行に必要なカーソルを宣言します。たとえば、次は、SQL_STMT
という名前の SQL 文の文字列で使用するカーソルとして DYN_CURSOR を宣言して
いる例です。
EXEC SQL
DECLARE DYN_CURSOR CURSOR FOR SQL_STMT;
2
宣言したカーソルを開きます。ここでは、入力 XSQLDA を指定します。
EXEC SQL
OPEN DYN_CURSOR USING SQL DESCRIPTOR in_sqlda;
SQL 文の文字列は、カーソルを開くと同時に実行されるとともに、行のアクティブ
セットが作成されます。カーソルとアクティブセットの詳細は、第 6 章「データ処理」
を参照してください。
3
ループを使って行を 1 行ずつ取り出し、各行について選択リスト(列のデータ)を処
理します。たとえば、次は、DYN_CURSOR カーソルを使って行を 1 行ずつ取り出す
とともに、各行について選択リスト項目の処理を行っているループの例です。ここで
は、アプリケーション独自の関数(process_column())を使って行の列のデータを処理
しています。
while (SQLCODE == 0)
{
EXEC SQL
FETCH DYN_CURSOR USING SQL DESCRIPTOR out_sqlda;
if (SQLCODE == 100)
break;
for (i = 0; i < out_sqlda->sqld; i++)
{
process_column(out_sqlda->sqlvar[i]);
}
}
4
全部の行の取り出しが完了したら、カーソルを閉じます。
EXEC SQL
CLOSE DYN_CURSOR;
第 13 章 動的 SQL
13-31
パラメータを持つクエリー SQL 文の文字列の再実行
PREPARE を使ってパラメータを持つクエリー SQL 文の文字列の準備ができたら、その
SQL 文をアプリケーションで繰り返し使用することができます。この場合、プレースホル
ダ(入力パラメータ)に新しいデータを入れるとともに NULL 値インジケータの設定を行
います。また、SQL 文から返されるデータの情報を出力 XSQLDA に渡します。さらに、
カーソルを閉じて、再度開く必要があります。
パラメータへの値の入力と NULL 値インジケータの設定は、この章の「パラメータを持つ
クエリー SQL 文の文字列の用意」の 3 から 5 までの手順を参照してください。
SQL 文によって返されるデータの情報を出力 XSQLDA に渡す処理は、この章の「パラ
メータを持つクエリー SQL 文の文字列の用意」の 6 から 8 までの手順を参照してくださ
い。
カーソルを再度開いて選択リスト項目を処理するには、この章の「クエリー SQL 文の文字
列のカーソルによる実行」のステップ 2 ~ 4 を繰り返してください。
13-32 埋 め 込 み S Q L ガ イ ド
第
章
前処理、コンパイル、リンク
第 14 章
この章では、gpre を使ってプログラムを前処理する方法と、前処理したプログラムをコン
パイルおよびリンクして実行できるようにする方法について説明します。
gpre プリプロセッサは、サーバー ライセンスを購入すると付いてきます。これは C/C++
上でのみ動作保証されていますが、他の多くの言語でも動作します。
前処理
SQL プログラムまたは動的 SQL(DSQL)プログラムをコーディングしたら、そのプログ
ラムをコンパイルする前に gpre で前処理する必要があります。gpre では、InterBase ライ
ブラリ関数呼び出しを生成することで、
SQL コマンドや DSQL コマンドをホスト言語のコ
ンパイラで処理できるステートメントに変換します。gpre では、SQL や DSQL のデータ
ベース変数をホスト言語のコンパイラで処理できる変数に変換し、それらの変数をホスト
言語の形式で宣言します。さらに、gpre では、SQLCODE 変数や DSQL で使用される拡
張 SQL ディスクリプタ エリア(XSQLDA)など、SQL に必要な特定の変数やデータ構造
も宣言します。
gpre の使用
gpre の構文は次のとおりです。
gpre [-language] [-options] infile [outfile]
infile 引数は入力ファイルの名前を指定します。
省略可能な outfile 引数は出力ファイルの名前を指定します。ファイルが指定されない
場合、gpre は入力ファイルと同じ名前のファイルに出力を送ります。この出力ファイルの
拡張子は入力ファイルの言語によって異なります。
第 14 章 前処理、コンパイル、リンク
14-1
前処理
gpre には、ソース プログラムの言語と他の多数のオプションを指定できるスイッチがあ
ります。これらのスイッチは、入出力ファイル指定の前か後に付けることができます。各
スイッチには少なくとも、スペースの後にハイフンとそのスイッチを指定する一意な文字
が含まれている必要があります。
言語スイッチ
言語スイッチは、ソース プログラムの言語を指定するものです。C と C++ はすべてのプ
ラットフォームで使用可能な言語です。これらのスイッチを以下の表に示します。
表 14.1 すべてのプラットフォームで使用可能な gpre 言語スイッチ
スイッチ
言語
-c
C
-cxx
C++
さらに、他の言語をサポートしているプラットフォームもあります。ただし、その言語用
の追加 InterBase ライセンスを購入した場合に限ります。これらの使用可能な言語とそれに
対応するスイッチの一覧を以下の表に示します。
表 14.2 追加の gpre 言語スイッチ
スイッチ
言語
-al[sys]
Ada(Alsys 版)
-a[da]
Ada(VERDIX 版、VMS 版、TeleSoft 版)
-ansi
ANSI-85 COBOL
-co[bol]
COBOL
-f[ortran]
FORTRAN
-pa[scal]
Pascal
たとえば、census.e という C プログラムを前処理するには、次のように入力します。
gpre -c census.e
14-2 埋 め 込 み S Q L ガ イ ド
前処理
オプション スイッチ
オプション スイッチは、前処理オプションを指定するものです。使用可能なスイッチを以
下の表で説明します。
表 14.3
gpre オプション スイッチ
スイッチ
説明
-charset name
コンパイル時に使用されるキャラクタ セットを限定します。なお、
name はキャラクタ セット名です。
-d[atabase] filename
プログラムのデータベースを宣言します。filename はアクセスす
るデータベースのファイル名です。このオプションを使用するの
は、プログラムに SQL 文が含まれていて、そのプログラムがデー
タベースそのものに接続していない場合です。プログラムにデー
タベース宣言が含まれている場合には、このオプションを使用し
ないでください。
-d_float
VAX/VMS の場合のみ使用可能です。倍精度データが D_FLOAT
形式でアプリケーションから渡され G_FLOAT 形式でデータベー
スに格納されることを指定します。データベース内でのデータ比
較は G_FLOAT 形式で実行されます。データベースからアプリ
ケーションに返されるデータは D_FLOAT 形式になります。
-e[ither_case]
gpre で大文字と小文字を両方とも認識できるようにします。コー
ド内に SQL キーワードが小文字で現われる場合は、常に
-either_case スイッチを使用します。大文字と小文字が混在してい
る場合、このスイッチを使用しないと、gpre で入力ファイルを処
理できません。C 以外の言語では、大文字と小文字が区別されな
いため、このスイッチは不要です。
-m[anual]
トランザクションを自動的に生成しないようにします。-m スイッ
チを使用するのは、独自のトランザクション処理を行う SQL プロ
グラムの場合と、本来独自のトランザクションを明示的に制御し
なければならないすべての DSQL プログラムの場合です。
-n[o_lines]
C プログラムの場合に、行番号を出力に含めません。
-o[utput]
gpre の出力をファイルではなく標準出力に対して行います。
-password password
データベース パスワードの入力が必要なデータベースにプログラ
ムから接続する場合は、そのパスワード password を指定します。
-r[aw]
BLR を同等のニーモニックではなく生の数値として出力します。
このオプションは、gpre の出力ファイルを小さくするのに役立つ
場合がありますが、ファイルが読みにくくなります。
-sql_dialect
SQL ダイアレクトを設定します。有効な値は 1、2、3 のいずれか
です。
第 14 章 前処理、コンパイル、リンク
14-3
前処理
表 14.3
gpre オプション スイッチ ( 続き )
スイッチ
説明
-user username
データベース ユーザー名の入力が必要なデータベースにプログラ
ムから接続する場合は、そのユーザー名 username を指定します。
-x handle
-database オプションで指定されたデータベース ハンドルの外部
宣言を有効にします。このオプションは、リンクされた別のモ
ジュールからグローバル宣言を取得するようにプログラムに指示
するものです。-d スイッチも併せて使用されている場合にのみ使用
します。
-z
gpre のバージョン番号と宣言されているすべてのデータベースの
バージョン番号を出力します。これらのデータベースは、プログラ
ム内か -database スイッチで宣言することができます。
適切なライセンスがあり C 以外の言語を使用しているサイトの場合は、以下の表に説明す
る追加の gpre オプションを指定することができます。
表 14.4 言語固有の gpre オプション スイッチ
スイッチ
説明
-h[andles] pkg
Ada ハンドル パッケージである pkg を指定します。
例
以下のコマンドでは、appl1.e というファイルの C プログラムを前処理しています。出
力ファイルは appl1.c になります。データベースが指定されていないため、ソース コード
でデータベースに接続する必要があります。
gpre -c appl1
以下のコマンドは前のコマンドと同じですが、ソース コードでデータベースを開くと仮定
するのではなくデータベース mydb.ib を明示的に宣言している点が異なります。
gpre -c appl1 -d mydb.ib
gpre クライアント ダイアレクトの設定
デフォルトでは、gpre に接続先データベースのダイアレクトを指定できます。これにより、
gpre で以前のソース ファイルを変更せずに解析することができます。異なるダイアレク
トのクライアントとして動作するように gpre を設定することができます。それには以下
のいずれかの方法を用います。
以下のように、オプション -sql_dialect n を付けて gpre を起動します(n は 1、2、
3 のいずれか)。
gpre -sql_dialect n
ソース コード内でダイアレクトを指定します。たとえば以下のように記述します。
EXEC SQL
SET SQL DIALECT n
14-4 埋 め 込 み S Q L ガ イ ド
前処理
gpre ダイアレクトの優先順位は以下のとおりです。
• 最低:接続しているデータベースのダイアレクト
• 中間:コマンド ラインで指定されたダイアレクト
• 最高:ソース コード内で指定されたダイアレクト
ファイル拡張子を用いた言語の指定
言語スイッチを用いてホスト言語を指定するだけでなく、ソース ファイルのファイル名拡
張子でホスト言語を指定することもできます。gpre でサポートしている言語ごとのファ
イル名拡張子と出力ファイルのデフォルト拡張子を以下の表に示します。
表 14.5 ファイル拡張子による言語指定
言語
入力ファイル拡張子
デフォルトの出力ファイル拡張子
Ada(VERDIX 版)
ea
a
Ada(Alsys 版、TeleSoft 版) eada
ada
C
e
c
C++
exx
cxx
COBOL
ecob
cob
FORTRAN
ef
f
Pascal
epas
pas
たとえば、census.ecob という COBOL プログラムを前処理するには、次のように入力
します。
gpre census_report.ecob
これで、census.cob という出力ファイルが生成されます。
ファイル名拡張子を指定する際には、以下のように、言語スイッチを併せて指定すること
もできます。
gpre -cob census.ecob
ソース ファイルの指定
gpre のコマンドラインでは言語スイッチもファイル名拡張子も省略可能なため、以下の 3
とおりの状況が発生する可能性があります。
• 言語スイッチと拡張子なしの入力ファイルが指定されている
• 言語スイッチが指定されておらず拡張子付きの入力ファイルが指定されている
• 言語スイッチもファイル拡張子も指定されていない
このセクションでは、これらの各場合での gpre の動作について説明します。
第 14 章 前処理、コンパイル、リンク
14-5
前処理
言語スイッチと拡張子なしの入力ファイルが指定されている場合
gpre では、コマンドラインに言語スイッチが指定されているが指定された入力ファイル
に拡張子がない場合、以下のように動作します。
1
拡張子のない入力ファイルを探します。そのファイルが見つかった場合、gpre はそれ
を処理し、適切な拡張子の付いた出力ファイルを生成します。
該当する入力ファイルが見つからない場合、gpre は、指定された言語に対応する拡張
子の付いたファイルを探します。そのようなファイルが見つかった場合は、適切な拡張
子の付いた出力ファイルを生成します。
2
指定されたファイルも適切な拡張子の付いた指定ファイルも見つからなかった場合、
gpre は以下のエラーを返します。
gpre: can’t open filename or filename.extension
filename は gpre コマンドで指定されたファイルです。extension は指定された
プログラムの言語固有のファイル拡張子です。
例
以下のコマンドが発行されたとしましょう。
gpre -c census
gpre は以下の一連のアクションを実行します。
1
拡張子の付いていない census というファイルを探します。そのファイルが見つかっ
たら、それを処理して、census.c を生成します。
2
census が見つからなかったら、census.e というファイルを探します。census.e が
見つかったら、そのファイルを処理して、census.c を生成します。
3
census も census.e も見つからなかったら、以下のエラーを返します。
gpre: can’t open census or census.e
言語スイッチが指定されておらず拡張子付きの入力ファイルが指定さ
れている場合
言語スイッチが指定されておらず拡張子付きの入力ファイルが指定されている場合、gpre
は指定されたファイルを探し、言語がその拡張子で指定されていると仮定します。
たとえば、以下のコマンドが処理されるとしましょう。
gpre census.e
gpre は census.e というファイルを探します。そのファイルが見つかった場合、gpre は
それを C プログラムとして処理し、census.c という出力ファイルを生成します。その
ファイルが見つからなかった場合、gpre は以下のエラーを返します。
gpre: can’t open census.e
14-6 埋 め 込 み S Q L ガ イ ド
コンパイルとリンク
言語スイッチもファイル拡張子も指定されていない場合
言語スイッチもファイル名拡張子も指定されていない場合、gpre は 以下の順序でファイ
ルを探します。
1
2
3
4
5
6
filename.e(C)
filename.epas(Pascal)
filename.ef(FORTRAN)
filename.ecob(COBOL)
filename.ea(VERDIX Ada)
filename.eada(Alsys 版および TeleSoft 版の Ada)
そのようなファイルが見つかった場合、gpre は適切な拡張子の付いた出力ファイルを生
gpre は以下のエラーを返します。
成します。そのようなファイルが見つからなかった場合、
gpre: can’t find <filename> with any known extension.
Giving up.
コンパイルとリンク
プログラムの前処理が終ったら、コンパイルとリンクを行う必要があります。コンパイル
すると、前処理されたソース ファイルからオブジェクト モジュールが生成されます。プロ
グラムをコンパイルするには、ホスト言語のコンパイラを使用します。リンク プロセスで
は、外部参照を解決し、実行可能オブジェクトを生成します。使用されるプラットフォー
ム、オペレーティング システム、ホスト言語に基づいてプログラムのオブジェクト モ
ジュールを他のオブジェクト モジュールやライブラリにリンクするには、指定されたプ
ラットフォームで使用可能なツールを使用します。
コードが埋め込み SQL プリプロセッサ gpre から生成された場合も、InterBase API を
使って作成された場合も、以下の手順が適用されます。アプリケーションは共有 GDS ライ
ブラリのみとリンクします。
Microsoft Windows の場合
Windows では、アプリケーションのプロジェクトを作成するには、コマンドライン コンパ
イルではなく、できるだけ IDE を使用します。
C++ Builder
bcc32 -a4 -tWM -tWC -I<InterBase_home>\SDK\include application.c
-eapplication.exe <InterBase_home>\SDK\lib\gds32.lib
Microsoft Visual C++(C および C++)
cl -W3 -G4 -Gd -MD -I<InterBase_home>\SDK\include application.c
<InterBase_home>\SDK\lib\gds32_ms.lib /Feapplication.exe
第 14 章 前処理、コンパイル、リンク
14-7
コンパイルとリンク
Solaris の場合
SPARCWorks 4.2(C)
cc -mt -w -I/usr/interbase/include -c application.c
cc -mt application.o -o application -lgdsmt
-lsocket -lthread -lsnl -ldl
SPARCWorks 4.2(C++)
CC -mt -w -I/usr/interbase/include -c application.C
CC -mt application.o -o application -lgdsmt
-lsocket -lthread -lsnl -ldl
Ada プログラムのコンパイル
Ada プログラムをコンパイルする場合は、必ず、Ada ライブラリにパッケージ
InterBase.ada(VERDIX Ada の場合は InterBase.a)が含まれている必要がありま
す。このパッケージは InterBase の include ディレクトリにあります。
InterBase の examples デ ィレクトリにあるプログラムを使用する場合は、やはり
examples デ ィ レ ク ト リ に あるパッケージ basic_io.ada(VERDIX Ada の場合は
basic_io.a)を使用してください。
UNIX 上でのリンク
Unix プラットフォームでは、プログラムを以下のライブラリとリンクすることができま
す。
• パイプを使用するライブラリ(-lgds オプションで取得)。このライブラリを利用すると、
リンクが速くなると共にイメージが小さくなります。さらに、InterBase の新しいバージョ
ンがインストールされたときにアプリケーションが自動的にそれと連動できるようになり
ます。
• パイプを使用しないライブラリ(-lgds_b オプションで取得)。このライブラリを利用する
と実行は速くなりますが、アプリケーションが InterBase の特定のバージョンでしか動作で
きなくなります。InterBase の新しいバージョンをインストールしたら、その新機能やその
バージョンで作成されたデータベースを使用できるようにプログラムをリンクし直す必要
があります。
SunOS-4 では、-lgdslib オプションを用いることで、プログラムを共有可能ライブラリ
とリンクすることができます。それにより、実行時に動的リンクが生成され、イメージが
小さくなると共に完全なライブラリをリンクしたときの実行スピードが得られます。さら
に、このオプションを指定すると、InterBase バージョンを自動的にアップグレードできる
ようになります。
特定プラットフォームでの InterBase リンク オプションの詳細については、InterBase
ディレクトリにあるオンライン readme を参照してください。
14-8 埋 め 込 み S Q L ガ イ ド
Fly UP