初めに
NL2SQL(自然言語でSQLを作る仕組み)を作っていると、ある壁に必ず当たります。
表名・列名・データ型だけでは、業務の意味は伝わらない。
例えば CUST_MST と ORD_TXN という2つの表があったとします。物理スキーマだけを見ると、STAT_CD が何のステータスなのか、AREA_CD = '01' が「関東」のことだなんて、LLMにはわかりません。
Oracle AI Database 26ai の Select AI では、こうした「業務の意味」をデータベース側に持たせる手段として、次の3つ(+1つ)が用意されています。
COMMENTANNOTATIONS- Domain(Data Use Case Domain)
- (ついでに)View / Materialized View
一見すると「表や列に説明を付ける」ことで同じに見えますが、実は役割がまったく違います。
この記事では、[shirok さんの検証記事]
(Oracle AI Database 26ai / Select AI で実機検証した2部構成の第2部)の内容をベースに、
- 3つそれぞれの役割と違い
- どんなシーンで何に使うとよいのか
- それぞれがSQL生成のどの精度を底上げするのか
を整理します。
Select AI が Metadata から SQL を生成する仕組みそのものについては、同じく shirok さんの第1部が非常に分かりやすいので、そちらを先に読むのがおすすめです。
結論
先に結論です。
COMMENT・ANNOTATIONS・Domain は「どれか1つ」の代替関係ではなく、役割が異なる3つのレイヤとして組み合わせるものです。
人向けの説明 -> COMMENT
AI/アプリ向けの意味 -> ANNOTATIONS
業務値の再利用・強制 -> Domain
問い合わせ構造の単純化 -> View / MV
精度への影響を一言でまとめるとこうです。
| 機能 | 一言で言うと | 精度を上げる対象 |
|---|---|---|
| COMMENT | 人向けの一言説明 |
コード値→業務意味の変換(A=有効、01=関東 等) |
| ANNOTATIONS | 用途別に分けた構造化メタデータ | 用語マッチング・JOIN判断・単位・粒度 |
| Domain | 業務上同じ値の「再利用可能なデータ型」 | 複数テーブル間の一貫性・制約の強制(単発精度より運用精度) |
| View / MV | AI向けの問い合わせ構造 | SQLの複雑さ自体を削減(生成ミスの素を減らす) |
shirok さんの検証では、以下のような結果が出ています(検証環境: Oracle AI Database 26ai EE 23.26.3.2.0、OpenAI / gpt-5.6-luna、SQLcl)。
| パターン | 「有効顧客は何人?」 | 「確定済み売上合計は?」 | 「関東の有効顧客の確定済み売上(顧客別)」 |
|---|---|---|---|
| 何もしない(基本Metadataのみ) | ×(STAT_CD='1' と誤推測して 0 人) |
○ 37,000円(たまたま当たり) | × 未定義のバインド変数が出て実行不能 |
| + COMMENT | ○ 3人 | ○ 37,000円 | ○ |
| + 直接Annotations | ○ 3人 | ○ 37,000円 | ○ |
| + Domain + 直接Annotations | ○ 3人 | ○ 37,000円 | ○ |
| AI向けView(構造変更) | - | - | ○(生成SQLは WHERE AREA_CD='01' のみ) |
つまり、COMMENT も Annotations も「業務用語とコード値の解釈」を直撃し、Domain は「一貫性と管理性」、View は「問題の単純化」を担う、という分担になります。
1. Select AI が何を見ているのか(前提の整理)
Select AI が LLM に送るものは、基本 Metadata(表名・列名・データ型)に、プロファイルの設定次第で COMMENT / Annotations / 制約情報が加わります。
"comments": true // 表・列の COMMENT をプロンプトに追加
"annotations": true // 表・列の Annotations をプロンプトに追加
"constraints": true // 主キー・外部キーなどの参照整合性情報を追加
この3つは デフォルトでは OFF なので、付与してもプロファイルで ON にしないと LLM には届きません。地味な落とし穴です。
確認の仕方も押さえておきます。
-- LLM に実際に渡る内容を確認
SELECT AI SHOWPROMPT <質問>;
-- 生成された SQL を確認
SELECT AI SHOWSQL <質問>;
SHOWPROMPT に Annotation 名と値が含まれていれば「LLM に届いた」と判定できます。ただし DOMAIN_NAME は表示されないので、「これが Domain 由来か直接付与か」の切り分けは USER_ANNOTATIONS_USAGE で行います(両者を突き合わせて検証するのが shirok さんの記事のやり方で、ここが丁寧で良い)。
2. COMMENT:人向けの「一行説明」
2.1 仕組み
COMMENT は昔ながらの「表・列に1文字列の説明文を付ける」機能です。
COMMENT ON TABLE cust_mst IS '顧客基本情報。1行=1顧客。';
COMMENT ON COLUMN cust_mst.stat_cd
IS '顧客ステータス。A=有効, I=休眠。';
COMMENT ON COLUMN ord_txn.amt
IS '受注1件の売上金額(日本円)。確定済みの判定は ORD_TXN.STAT_CD で行う。';
- 表・列に 1件だけ 付与できる自由な文字列
- Oracle Database 自身が内容を解釈・強制はしない(メモに過ぎない)
- IDE、設計書生成ツール、データカタログなど「人が見る側」の標準的な説明欄
- View 全体に付ける場合は
COMMENT ON TABLE view_name IS '...'(COMMENT ON VIEWは存在しない) - Domain への COMMENT は存在しない(Domain 自体の説明は Annotations)
確認は USER_TAB_COMMENTS / USER_COL_COMMENTS。削除は空文字列を設定します(COMMENT ON COLUMN ... IS '';)。
2.2 どの精度が上がるか
検証で効いたのは 「コード値の読み替え」 です。
COMMENT なしの状態で「有効顧客は何人ですか?」と聞くと、LLM は STAT_CD = '1' と勝手に推測して 0人 という誤答になります。一方、「A means active」と COMMENT に書いておくだけで STAT_CD = 'A' で正しく 3人を返します。
精度観点: 「値の意味」を伝えることによる WHERE 条件の正確化。
2.3 向いているシーン・限界
- 向いている: 表・列の用途を「人間が5秒で理解できる」レベルで説明したいとき。既存のドキュメント生成・データカタログとも相性が良い
- 限界: 1列に1文書しか書けない。別名・単位・JOIN先・コード値一覧を用途別に分けたくても、全部1つの文章に詰め込むしかない
3. ANNOTATIONS:AI向けの「構造化メタデータ」
3.1 仕組み
Annotations は 名前/値のペアを複数個 付ける仕組みです。Annotation 名は最初に使った時点で自動登録されるので、事前に Domain へ辞書登録する必要はありません。表・列だけでなく View・View列にも直接付与可能です。
ALTER TABLE cust_mst
MODIFY stat_cd ANNOTATIONS (
"DESCRIPTION" 'Customer status code.',
"ALIASES" 'customer status, active customer, 顧客状態, 有効顧客',
"VALUES" 'A = active (有効); I = inactive (休眠).'
);
ALTER TABLE ord_txn
MODIFY cust_id ANNOTATIONS (
"DESCRIPTION" 'Customer identifier.',
"JOIN COLUMN" 'Join with CUST_MST.CUST_ID.'
);
Oracle の AI Enrichment ガイドで推奨されているラベルは、次の5つです(予約語ではなく推奨の命名規則)。
| Annotation | 用途 |
|---|---|
DESCRIPTION |
表・列の業務的な意味、1行の粒度 |
ALIASES |
別名・同義語・ユーザーが実際に使う呼び方 |
UNITS |
通貨・距離・重量などの単位 |
JOIN COLUMN |
推奨される JOIN 先と、その関係の意味 |
VALUES |
コード値、代表値、値の意味 |
実運用で踏むポイント:
- 名前: 最大1,024文字 / 値: 最大4,000文字
- 同名 Annotation を重複 ADD すると
ORA-11552。更新はREPLACE、冪等にしたいならADD IF NOT EXISTS - 値の意味はデータベースが強制しない(制約ではない)
- 自由度が高い分、
DESCRIPTION/DESC/BUSINESS_DESCとラベル名の表記揺れが起きやすい。命名・言語・値の形式をチームで先に標準化する
3.2 どの精度が上がるか
COMMENT と比べると「構造」の差が効いてきます。
| ラベル | 精度を上げるポイント |
|---|---|
ALIASES |
「顧客番号」「取引先ID」「売上」といったユーザーの言い回しが物理列名と一致しない問題を解決。テーブル・列の選択精度が上がる |
VALUES |
STAT_CD='A' が「有効」だと確実に分かる。WHERE 条件の値変換精度が上がる |
JOIN COLUMN |
どの列同士を JOIN すべきか明示。物理 FK 情報をプロンプトに入れない(constraints: false)状態で JOIN を正しく引ける |
UNITS |
金額の単位(日本円等)の取り違えを防ぐ |
DESCRIPTION |
「1行=1受注」のような粒度情報を伝え、集計方法の誤りを防ぐ |
shirok さんの検証では、直接 Annotations のみのパターンでも3問すべて正解でした。
3.3 向いているシーン・限界
- 向いている: 別名だらけの業務システム、コード値が多い列、AI だけが使う説明を既存 COMMENT から分離したいとき
- 限界1: 再利用できない。同じ意味の別名を10個のテーブルの同じ列に付けたいと、10回分書き直す(更新漏れが起きる)
- 限界2: 自由度が高すぎてラベルが揺れる。標準化しないとかえって LLM を混乱させる
4. Domain:業務上の「データ型」として再利用する
4.1 仕組み
Domain は「業務上の値」を表す スキーマ・オブジェクトです。「タグを付ける機能」ではなく、型・制約・デフォルト・表示・並び順・Annotations をまとめた再利用可能な定義だと思っています。
CREATE DOMAIN customer_status_d AS CHAR(1)
CONSTRAINT customer_status_d_ck CHECK (VALUE IN ('A', 'I'))
ANNOTATIONS (
"DESCRIPTION" 'Customer status code.',
"ALIASES" 'customer status, active customer, 顧客状態, 有効顧客',
"VALUES" 'A = active (有効); I = inactive (休眠).'
);
CREATE DOMAIN sales_area_d AS CHAR(2)
CONSTRAINT sales_area_d_ck CHECK (VALUE IN ('01', '02'))
ANNOTATIONS (
"DESCRIPTION" 'Sales area code assigned to a customer.',
"ALIASES" 'sales area, region, 営業地域, 地域',
"VALUES" '01 = Kanto (関東); 02 = Kansai (関西).'
);
既存の列に適用すると、Domain に付けた Annotations が その列へ自動継承されます。
ALTER TABLE cust_mst MODIFY (stat_cd) ADD DOMAIN customer_status_d;
ALTER TABLE ord_txn MODIFY (amt) ADD DOMAIN sales_amount_d;
継承を確認するには USER_ANNOTATIONS_USAGE を見ます。
SELECT object_name, object_type, column_name,
domain_owner, domain_name, -- NULL=直接付与 / 値あり=Domain由来
annotation_name, annotation_value
FROM user_annotations_usage
ORDER BY object_name, column_name, annotation_name;
4.2 どの精度が上がるか
正直に書くと、単発の SQL 生成精度が Annotations のみより上がる、とは限りません(shirok さんの検証でも、直接 Annotations と Domain+直接 Annotations で結果は同点でした)。
Domain が効くのは別の次元です。
-
一貫性: 「顧客ID」の意味・別名を
customer_id_dに1つだけ定義し、CUST_MST.CUST_IDとORD_TXN.CUST_ID両方に適用。定義箇所が1つになる -
強制力:
CHECK (VALUE IN ('A','I'))は Annotations と違いデータベースが強制する。LLM に「A か I」と言わなくても、不正値は入らない - 管理性: 複数テーブルで共通する「顧客ID」「通貨」「メール形式」の定義を1箇所で変更管理できる
精度観点: 単発の正解率より、「定義が複数箇所に分散してズレる事故」を構造的に減らす運用精度。
4.3 使わないほうがよい場面・注意点
- Domain はタグ集ではない。1列に付けられる Domain は 1つだけ。Customer / PII / Gold のように複数タグを振りたい場合は Annotations を使う
-
適用前に互換性を確認。型・長さ・精度・スケール(
STRICTなら完全一致)・デフォルト値・照合順序が噛み合わないと関連付けできない。「Annotations を付けたい」だけで Domain を作るのではなく、「Domain の制約やデフォルト値ごとその列に適用してよいか」で判断する -
View 列に
ADD DOMAINする DDL は存在しない(単純投影列はベース表の Domain 情報を保持する) -
Domain 列レベルの Annotation は
ALTER DOMAINでは変更できない(Domain の再作成が必要)。オブジェクトレベルと列レベルで同名 Annotation を付けると、参照先列に同名 Annotation が重複表示される - Domain 由来の制約・表示式・並び順が Select AI のプロンプトに自動追加されるかは、公式資料には明記されていない。実際に
SHOWPROMPTで確認するのが安全
5. 補足: View で「問題そのもの」を単純化する
shirok さんの検証で最も印象的だったのがこの部分です。
「関東の有効顧客について、確定済み売上金額を顧客別に表示してください」に対する生成 SQL は、ベース表を直接使うパターンだとこんな感じです。
SELECT c.CUST_NM, SUM(o.AMT) AS CONFIRMED_SALES_AMT
FROM CUST_MST c
JOIN ORD_TXN o ON o.CUST_ID = c.CUST_ID
WHERE c.AREA_CD = '01'
AND c.STAT_CD = 'A'
AND o.STAT_CD = 'C'
GROUP BY c.CUST_ID, c.CUST_NM;
JOIN、2つのステータス条件、SUM、GROUP BY を全部 LLM に考えさせられています。これを「AI 問い合わせ用 View」で吸収すると、
-- View 側で JOIN・有効顧客・確定済みの条件・集計を実装済み
SELECT c.CUST_ID, c.CUST_NM, c.CONFIRMED_SALES_AMT
FROM CUST_CONFIRMED_SALES_V c
WHERE c.AREA_CD = '01';
と、WHERE 条件1つだけの SQL になります。業務ロジックを View の SQL に移したことで、LLM が間違える箇所自体が減ったわけです。View 列にも Annotations(DESCRIPTION / ALIASES / UNITS)を直接付けられるので、算出列の意味も伝えられます。
精度観点: メタデータで「伝える」のではなく、SQL の複雑さ自体を減らす。これがいちばん確実。
注意点として、Domain を持つ列を単純投影した View 列は Domain 由来の Annotation を保持する一方、View 列に同名 Annotation を直接付けると重複します(検証では 17件 → 重複5件を削除して12件に整理)。Domain を保持する単純投影列には同じ Annotation を重ねず、View 固有の意味(粒度・算出列・単位)だけ直接付与するのが整理しやすそうです。
6. 実運用での設計方針(まとめ)
| 伝えたいこと | 置く場所 | 例 |
|---|---|---|
| 人向けの短い基本説明 | COMMENT | 表の用途、列の概要 |
| 表・列固有の業務的な意味 | 直接 Annotations | 粒度、別名、コード値 |
| AI へ伝える推奨 JOIN・関係 | 直接 Annotations(JOIN COLUMN) |
「この列は ORD_TXN.CUST_ID と JOIN する」 |
| 複数テーブルで共通する安定した業務値 | Domain | 顧客ID、通貨、メールアドレス |
| 厳密に守るべき値のルール | Domain の CHECK / PK / FK | ステータス値の制限、金額の非負 |
| JOIN・計算・集計・公開粒度 | View / MV | 顧客別売上、月次売上 |
| View 固有の意味 | View 列への直接 Annotations | 1行の粒度、算出列の意味、単位 |
原則は2つです。
- 共通で安定した意味とルール → Domain。オブジェクト固有の文脈 → 直接 Annotations。人の説明 → COMMENT
- 「説明」と「強制すべきルール」を分ける。Annotation に「売上は確定済み受注の税引前金額」と書いても、DB はそれを強制しません。常に同じ結果でなければならない計算は View の SQL や制約で実装し、Annotation は「その実装の意味を AI に伝える」ために使う
運用面の注意
- COMMENT と DESCRIPTION の二重管理: 既存 COMMENT を消して Annotations 一本にする必要はない(IDE やデータカタログがまだ COMMENT を参照している可能性)。ただし「どちらを正本とするか・更新ルール」を決め、矛盾した説明を LLM に渡さないこと。Select AI で両方を ON にした場合の優先順位は、shirok さんの記事時点で公式ドキュメントに明記されていません
-
ラベル名を標準化する:
DESCRIPTION/ALIASES/UNITS/JOIN COLUMN/VALUESを基本セットにし、独自ラベルを追加するなら名前・適用対象・値の書式・記述言語・更新責任者をチームで決める -
日本語の業務用語: Oracle は Annotations を明確な英語で書くことを推奨。日本語で質問が来る環境では、
ALIASES/VALUESに日本語の呼び方を併記するのが実用的('顧客状態, 有効顧客'のような形)。英語のみ / 日本語のみ / 併記、どれが精度高いかはモデル依存なので要検証 - 秘密情報を書き込まない: COMMENT / Annotations はデータディクショナリから読めるし、プロファイル設定次第で LLM へ渡ります
- DDL として変更管理する: COMMENT・Annotations・Domain・View はすべて DDL。Git で管理し、テーブル定義と一緒にレビューできる形に
最後に
今回の整理で、腑に落ちたポイントを3つ。
-
「精度を上げる」の正体は、業務用語とコード値の解釈。表名・列名・型だけでは推測できない
STAT_CD='A'のような意味を、COMMENT か Annotations で LLM に渡せるか、が最初の分岐点 - Domain の価値は再利用と強制。1問の正解率を上げる機能ではなく、複数テーブルに散らばる「顧客ID」の定義を1箇所に集め、制約まで強制する仕組み
- 一番効くのは場合によっては View。メタデータを整備するより先に、「AI に見せる構造」をどう設計するかが効く
「AI Ready Data」と言うと Vector 化や RAG ばかりが語られがちですが、データが何を意味し、どの値を使い、どう結合し、どの粒度で問い合わせるのかを Database 側に整えることも、ちゃんとした AI Ready Data の一部です。
プロンプト側をいじるより、スキーマ側に意味を持たせる。これが Select AI を使う上での一番の視点の転換でした。