はじめに
前回は、SELECT AIへ見せるテーブルをobject_listで業務単位に絞り込む方法を扱いました。
【SELECT AIの精度が出ない時に試したい5つのポイント】①対象表の絞り込み
しかし、対象テーブルを正しく選べるようにしても、次のようなSQLが生成されることがあります。
- 受注日ではなく、更新日時で期間を絞り込む
- 受注金額を、数量×単価の合計ではなく単価の合計で求める
- 「確定」という言葉を、実際には存在しない文字列
'CONFIRMED'へ変換する - 名前が似ている列同士をJOINする
- 「得意先」と
CUSTOMERSが同じ概念だと判断できない
この問題は、LLMから見るとテーブル名とカラム名だけでは業務上の意味が足りないことから起こります。
たとえばSTATUS_CODEというカラムがあっても、'20'が「確定」、'90'が「取消」を意味することは、物理名とデータ型だけからは分かりません。ORDER_DATEも、受注日、登録日、更新日、出荷日のどれなのかを明示しなければ誤解される可能性があります。
そこで本記事では、次の3つを使ってデータベース側のメタデータを補強します。
- テーブル・カラムコメント
- Oracle AI Database 26aiのアノテーション
- 主キー・外部キーなどの制約
そのうえで、AIプロファイルのcomments、annotations、constraintsを有効にし、SELECT AIへ業務上の意味とテーブル間の関係を渡します。
本記事の想定環境
本記事の設定例は、2026年9月時点の次の環境を想定しています。
- Autonomous AI Database Serverless 26ai
- AI providerとしてOCI Generative AIを使用
-
DBMS_CLOUD_AIでAIプロファイルを管理 -
【SELECT AIの精度が出ない時に試したい5つのポイント】①対象表の絞り込みで作成した
SALES_ORDER_AIプロファイルを使用 - 説明用の
SALESスキーマを使用
本文のスキーマ名、テーブル名、カラム名、コード値は説明用です。実際に試す際は、自分の環境に存在するオブジェクトと正しい業務定義へ置き換えてください。
説明を簡潔にするため、本記事ではSALESユーザーが説明用テーブルとSALES_ORDER_AIを所有しているものとします。メタデータの登録、USER_*ビューによる確認、AIプロファイルの更新はSALESユーザーで実行します。AIプロファイルを別のユーザーが所有している場合、SET_ATTRIBUTESはその所有者が実行し、メタデータは対象オブジェクトを参照できるユーザーからALL_*ビューと所有者条件を使って確認してください。
アノテーションやプロファイル属性の対応状況は、データベースのバージョンやサービス形態によって異なります。導入前にOracle AI Database Select AI Capability Matrixを確認してください。
この記事で伝えたいこと
先に結論をまとめます。
- 物理名だけで分からない業務上の意味を、テーブル・カラムコメントで説明する
- コード値、単位、日付の役割、同義語を具体的に記述する
- 複数の名前付き情報として管理したい場合は、26aiのアノテーションを使う
- コメントとアノテーションへ同じ情報を機械的に重複させない
- JOINに使う関係は、可能であれば主キー・外部キーとしてデータベースに定義する
- AIプロファイルで
comments、annotations、constraintsを有効にする -
SHOWPROMPTとSHOWSQLで、メタデータが利用されているか確認する - 同じ質問セットを使い、変更前後のSQLを比較する
コメントやアノテーションを増やせば、必ず正しいSQLになるわけではありません。同じ説明の重複や、SQL生成に不要な情報まで増やすと、かえって重要な情報が埋もれ、精度や一貫性が低下する可能性もあります。目的は、LLMへ渡す情報量を増やすことではなく、推測しなければならない部分を必要最小限の情報で減らすことです。
なぜテーブル名とカラム名だけでは足りないのか
SELECT AIは、自然言語の質問に関連するデータベースメタデータを使ってプロンプトを拡張します。
Oracle公式ドキュメントでは、テーブル名、カラム名、データ型に加え、オプションでテーブル・カラムコメント、アノテーション、制約をNL2SQLのプロンプトへ含められると説明されています。
一方、通常のNL2SQLでは、テーブルに格納されている実際の行やカラム値が、SQL生成用のプロンプトへ自動的に渡されるわけではありません。
したがって、次のような情報は明示的にメタデータへ記述する必要があります。
| 伝えたい情報 | 例 |
|---|---|
| テーブルの役割 |
ORDER_HEADERSは受注ヘッダーであり、1行が1受注を表す |
| カラムの業務名 |
CUSTOMER_NAMEは顧客名であり、得意先名、取引先名と同義 |
| コード値 |
STATUS_CODEの20は確定、90は取消 |
| 単位 |
UNIT_PRICEは1個あたりの税抜受注単価で、通貨は円 |
| 日付の意味 |
ORDER_DATEは登録日時ではなく受注日 |
| JOIN経路 |
ORDER_HEADERS.CUSTOMER_IDからCUSTOMERS.CUSTOMER_IDへ結合する |
| 集計粒度 |
ORDER_LINESは1行が1受注明細を表す |
メタデータ・エンリッチとは、データベースの実データを大量にLLMへ見せることではありません。SQL生成に必要な業務上の意味を、データベースメタデータとして整備することです。
今回使用する説明用データモデル
本記事では、次のテーブルがすでに存在するものとします。
| テーブル | 主なカラム | 役割 |
|---|---|---|
CUSTOMERS |
CUSTOMER_ID、CUSTOMER_NAME
|
顧客マスタ |
ORDER_HEADERS |
ORDER_ID、CUSTOMER_ID、ORDER_DATE、STATUS_CODE
|
受注ヘッダー |
ORDER_LINES |
ORDER_ID、LINE_NO、PRODUCT_ID、QUANTITY、UNIT_PRICE
|
受注明細 |
PRODUCTS |
PRODUCT_ID、PRODUCT_NAME
|
商品マスタ |
この例では、STATUS_CODEはVARCHAR2、ORDER_DATEはDATE、QUANTITYとUNIT_PRICEはNUMBERを想定します。そのため、コード値の20は文字列リテラル'20'として比較します。
確認に使う質問は、次のとおりです。
2026年の確定受注金額を得意先別に集計して
この質問へ正しく答えるには、少なくとも次の業務定義が必要です。
- 「得意先」は
CUSTOMERSの顧客を指す - 「確定」は
ORDER_HEADERS.STATUS_CODE = '20'を指す - 受注金額は
ORDER_LINES.QUANTITY * ORDER_LINES.UNIT_PRICEの合計とする - 金額は税抜・円建てとし、値引きと税は考慮しない
- 2026年の判定には
ORDER_HEADERS.ORDER_DATEを使用する - 受注ヘッダーと明細は
ORDER_ID、顧客とはCUSTOMER_IDで結合する - 顧客名が同じ別顧客をまとめないよう、顧客ID単位で集計する
これらは、本記事における生成SQLの評価基準です。実際の業務では、税、値引き、取消、通貨、会計期間などの定義を業務担当者と確認してください。
コメント・アノテーション・制約の役割を分ける
3つの仕組みは、同じ目的の別名ではありません。
| 仕組み | 向いている情報 | 特徴 |
|---|---|---|
| コメント | 業務上の説明、コード値、単位、同義語 | テーブルまたはカラムごとに1つの自由記述を保持する |
| アノテーション | 名前付きの業務メタデータ | 1つの対象へ複数の名前と値を付与できる |
| 制約 | 主キー、外部キー、参照整合性 | テーブル間の関係を構造として定義し、データ整合性にも関与する |
コメントは「人が読んでも分かる説明」にする
コメントには、物理名の日本語訳だけでなく、SQL生成時の判断材料を書きます。
悪い例:
STATUS_CODE: ステータスコード
ORDER_DATE: 受注日
これでは、物理名から得られる情報とほとんど変わりません。
改善例:
STATUS_CODE: 受注状態コード。10=受付、20=確定、30=出荷済、90=取消
ORDER_DATE: 受注日。受注実績を期間集計するときに使用する
アノテーションは情報を名前ごとに分ける
アノテーションは、名前とオプションの値からなる自由形式のメタデータです。たとえば、DISPLAY_NAME、BUSINESS_TERMS、ALLOWED_VALUESのように役割を分けられます。
Oracle公式ドキュメントでは、アノテーションはアプリケーション・メタデータをデータベースで一元管理し、複数のアプリケーション、モジュール、マイクロサービスから共有するための仕組みと説明されています。
アノテーションの名前にOracle共通の業務語彙が自動的に割り当てられるわけではありません。チーム内で命名規則と値の書き方を決め、一貫して使用します。
制約は「説明」ではなく「構造」にする
コメントへ「CUSTOMER_IDでJOINする」と書くこともできます。しかし、実際に主キーと外部キーを定義できるなら、関係を文章だけに閉じ込めず、データベースの構造として表現した方が明確です。
Oracle公式のSelect AI使用例でも、constraints=trueにより外部キーと参照キーの情報をLLMへ渡し、正確なJOIN条件の生成に利用する方法が紹介されています。
ただし、実態と異なる外部キーをSELECT AI向けのヒントとして追加してはいけません。制約はデータ整合性にも影響するため、実際のデータモデルを正しく表すものだけを定義します。
1. テーブル・カラムコメントを追加する
最初に、SQL生成へ直接役立つ説明をコメントとして追加します。
初めて試す場合は、まずコメントだけを登録してcomments=trueにし、SHOWPROMPTとSHOWSQLで変化を確認できます。アノテーションは名前付きメタデータを管理したい場合に追加し、制約は既存データとアプリケーションへの影響を確認してから適用します。
コメント追加SQLを表示する
COMMENT ON TABLE sales.customers IS
'顧客マスタ。顧客は得意先、取引先とも呼ぶ。1行が1顧客を表す';
COMMENT ON COLUMN sales.customers.customer_id IS
'顧客を一意に識別するID。顧客単位の集計キーとして使用する';
COMMENT ON COLUMN sales.customers.customer_name IS
'顧客名。得意先名、取引先名と同義。同名の別顧客が存在し得る';
COMMENT ON TABLE sales.order_headers IS
'受注ヘッダー。1行が1受注を表す';
COMMENT ON COLUMN sales.order_headers.order_id IS
'受注を一意に識別するID。ORDER_LINES.ORDER_IDとのJOINに使用する';
COMMENT ON COLUMN sales.order_headers.customer_id IS
'受注した顧客のID。CUSTOMERS.CUSTOMER_IDとのJOINに使用する';
COMMENT ON COLUMN sales.order_headers.order_date IS
'受注日。受注実績を期間集計するときに使用する';
COMMENT ON COLUMN sales.order_headers.status_code IS
'受注状態コード。10=受付、20=確定、30=出荷済、90=取消';
COMMENT ON TABLE sales.order_lines IS
'受注明細。1行が1受注内の1明細を表す。受注金額は受注数量と税抜受注単価を掛けて集計する';
COMMENT ON COLUMN sales.order_lines.order_id IS
'受注ID。ORDER_HEADERS.ORDER_IDとのJOINに使用する';
COMMENT ON COLUMN sales.order_lines.line_no IS
'受注内の明細番号。ORDER_IDと組み合わせて受注明細を一意に識別する';
COMMENT ON COLUMN sales.order_lines.product_id IS
'受注した商品のID。PRODUCTS.PRODUCT_IDとのJOINに使用する';
COMMENT ON COLUMN sales.order_lines.quantity IS
'受注数量。単位は個';
COMMENT ON COLUMN sales.order_lines.unit_price IS
'1個あたりの税抜受注単価。通貨は円';
COMMENT ON TABLE sales.products IS
'商品マスタ。1行が1商品を表す';
COMMENT ON COLUMN sales.products.product_id IS
'商品を一意に識別するID。商品単位の集計キーとして使用する';
COMMENT ON COLUMN sales.products.product_name IS
'商品名';
COMMENT文で登録した内容はデータディクショナリに保存されます。既存コメントへ新しいCOMMENT文を実行すると内容が置き換わるため、現在のコメントを確認してから適用します。
コメントを確認する
テーブルコメントはALL_TAB_COMMENTS、カラムコメントはALL_COL_COMMENTSで確認できます。
SELECT
owner,
table_name,
comments
FROM all_tab_comments
WHERE owner = 'SALES'
AND table_name IN ('CUSTOMERS', 'ORDER_HEADERS', 'ORDER_LINES', 'PRODUCTS')
ORDER BY table_name;
SELECT
owner,
table_name,
column_name,
comments
FROM all_col_comments
WHERE owner = 'SALES'
AND table_name IN ('CUSTOMERS', 'ORDER_HEADERS', 'ORDER_LINES', 'PRODUCTS')
AND comments IS NOT NULL
ORDER BY table_name, column_name;
コメントへ何を書くか
コメントは長ければよいわけではありません。SQL生成時に判断が分かれる情報を優先します。
- テーブルが保持する業務データと1行の粒度
- カラムの業務名と同義語
- 日付カラムが表すイベント
- 金額や数量の単位、税込・税抜、符号の意味
- コード値と業務上の表示名
- 集計に使用する式や対象外条件
- JOINに使うキー
逆に、設計書をそのまま貼り付けた長文や、SQL生成に関係しない運用履歴は避けます。矛盾した説明が複数あると、LLMへ渡す情報を増やしても判断は安定しません。
2. 26aiのアノテーションを追加する
次に、同じ対象へ複数の名前付きメタデータを付与します。
以下は説明用の命名例です。DISPLAY_NAMEやBUSINESS_TERMSは、本記事で定めた名前であり、Oracleが値の意味を固定している予約済みプロパティではありません。
アノテーション追加SQLを表示する
ALTER TABLE sales.customers ANNOTATIONS (
ADD OR REPLACE DISPLAY_NAME '顧客',
ADD OR REPLACE BUSINESS_TERMS '顧客,得意先,取引先'
);
ALTER TABLE sales.order_headers ANNOTATIONS (
ADD OR REPLACE DISPLAY_NAME '受注ヘッダー',
ADD OR REPLACE ROW_GRAIN '1行が1受注'
);
ALTER TABLE sales.order_headers MODIFY (
order_date ANNOTATIONS (
ADD OR REPLACE DISPLAY_NAME '受注日',
ADD OR REPLACE TIME_ROLE '受注実績の期間条件に使用する日付'
)
);
ALTER TABLE sales.order_headers MODIFY (
status_code ANNOTATIONS (
ADD OR REPLACE DISPLAY_NAME '受注状態',
ADD OR REPLACE ALLOWED_VALUES '10=受付,20=確定,30=出荷済,90=取消'
)
);
ALTER TABLE sales.order_lines MODIFY (
quantity ANNOTATIONS (
ADD OR REPLACE DISPLAY_NAME '受注数量',
ADD OR REPLACE MEASUREMENT_UNIT '個'
)
);
ALTER TABLE sales.order_lines MODIFY (
unit_price ANNOTATIONS (
ADD OR REPLACE DISPLAY_NAME '税抜受注単価',
ADD OR REPLACE MEASUREMENT_UNIT '円/個'
)
);
既存オブジェクトへアノテーションを追加または更新するには、そのオブジェクトを所有しているか、必要なALTER権限を持っている必要があります。
アノテーションを確認する
現在のスキーマが所有するアノテーションは、USER_ANNOTATIONS_USAGEで確認できます。
SELECT
object_name,
object_type,
column_name,
annotation_name,
annotation_value
FROM user_annotations_usage
WHERE object_name IN ('CUSTOMERS', 'ORDER_HEADERS', 'ORDER_LINES')
ORDER BY object_name, column_name NULLS FIRST, annotation_name;
別スキーマを含めて、現在のユーザーが参照可能なアノテーションを確認する場合はALL_ANNOTATIONS_USAGEを使用します。
コメントとアノテーションをどう使い分けるか
両方に同じ文章を機械的に複製する必要はありません。
たとえば、次のように役割を分けられます。
- コメント:人が読んで理解できる一続きの業務説明
- アノテーション:表示名、同義語、単位、粒度などを名前ごとに管理する情報
本記事では両方式の記述例を示すため、同義語、コード値、単位の一部を意図的に重複させています。実際の運用では、管理目的に応じて役割を分け、不要な重複を避けます。
矛盾がなければ重複しても問題ない、とは限りません。同じ内容をコメントとアノテーションの両方から送ると、プロンプト内の情報が過剰になり、SQL生成に必要な情報が相対的に埋もれます。その結果、入力トークンが増えるだけでなく、精度や生成結果の一貫性が低下する可能性もあります。
コメントだけで意味を十分に伝えられる場合は、同じ説明をアノテーションへ複製する必要はありません。アノテーションは、同義語や単位などを名前付きで管理する必要がある場合に限定する、といった方針を決めます。両方を利用する場合も、SHOWPROMPTで重複を確認し、commentsとannotationsを切り替えながら固定質問で比較します。
また、同じカラムへ矛盾した定義を書かないことも重要です。STATUS_CODEの20を、コメントでは「確定」、アノテーションでは「出荷済」と記述すると、精度改善どころか新しい曖昧さを増やします。
3. 主キー・外部キーを定義する
JOINの精度を上げるため、実際のデータモデルに対応する主キーと外部キーを定義します。
以下のSQLは、対象の制約がまだ存在しないことを前提とした説明例です。そのまま実行せず、既存の制約名、重複データ、孤立データ、運用上の影響を確認してください。
主キー・外部キー追加SQLを表示する
ALTER TABLE sales.customers
ADD CONSTRAINT pk_customers
PRIMARY KEY (customer_id);
ALTER TABLE sales.order_headers
ADD CONSTRAINT pk_order_headers
PRIMARY KEY (order_id);
ALTER TABLE sales.products
ADD CONSTRAINT pk_products
PRIMARY KEY (product_id);
ALTER TABLE sales.order_lines
ADD CONSTRAINT pk_order_lines
PRIMARY KEY (order_id, line_no);
ALTER TABLE sales.order_headers
ADD CONSTRAINT fk_order_headers_customer
FOREIGN KEY (customer_id)
REFERENCES sales.customers (customer_id);
ALTER TABLE sales.order_lines
ADD CONSTRAINT fk_order_lines_header
FOREIGN KEY (order_id)
REFERENCES sales.order_headers (order_id);
ALTER TABLE sales.order_lines
ADD CONSTRAINT fk_order_lines_product
FOREIGN KEY (product_id)
REFERENCES sales.products (product_id);
ここでは、ORDER_LINESの主キーをORDER_IDとLINE_NOの組にしています。ORDER_IDだけを一意キーと誤認すると、1受注に複数明細を保持できず、集計粒度の理解も崩れます。
制約はSELECT AI専用の設定ではありません。既存データが制約を満たさない場合は追加に失敗し、アプリケーションの更新処理にも影響します。DBAやデータモデルの管理者と確認し、通常のスキーマ変更として適用します。
また、CHECK (status_code IN ('10', '20', '30', '90'))のようなチェック制約だけでは、各値の業務上の名称までは表現できません。「20は確定」という対応は、コメントやアノテーションで別途説明します。
外部キーの対応を確認する
同一スキーマ内の外部キーは、次のSQLで子カラムと親カラムの対応を確認できます。
SELECT
fk.constraint_name,
fk.table_name AS child_table,
fkc.column_name AS child_column,
pk.table_name AS parent_table,
pkc.column_name AS parent_column
FROM user_constraints fk
JOIN user_cons_columns fkc
ON fkc.constraint_name = fk.constraint_name
JOIN user_constraints pk
ON pk.constraint_name = fk.r_constraint_name
JOIN user_cons_columns pkc
ON pkc.constraint_name = pk.constraint_name
AND pkc.position = fkc.position
WHERE fk.constraint_type = 'R'
AND fk.table_name IN ('ORDER_HEADERS', 'ORDER_LINES')
ORDER BY fk.constraint_name, fkc.position;
ALL_CONSTRAINTSでは、現在のユーザーがアクセスできるテーブルの制約定義を確認できます。
AIプロファイルでメタデータを有効にする
コメント、アノテーション、制約をデータベースへ登録しただけでは、すべてが自動的にNL2SQLのプロンプトへ含まれるとは限りません。AIプロファイル側でも利用するメタデータを指定します。
第1回で作成したSALES_ORDER_AIへ、3つの属性を追加します。
DBMS_CLOUD_AI.SET_ATTRIBUTESでAIプロファイルを変更できるのは、そのAIプロファイルの所有者です。本記事の前提と異なり、SALES_ORDER_AIを別のユーザーが作成している場合は、その所有者で次の処理を実行します。
BEGIN
DBMS_CLOUD_AI.SET_ATTRIBUTES(
profile_name => 'SALES_ORDER_AI',
attributes => q'~{
"comments": "true",
"annotations": "true",
"constraints": "true"
}~'
);
END;
/
新しくAIプロファイルを作成する場合は、同じ3属性をDBMS_CLOUD_AI.CREATE_PROFILEのattributesへ含めます。
| 属性 | SELECT AIへ追加する情報 |
|---|---|
comments |
テーブルおよびカラムのコメント |
annotations |
26aiのテーブルレベルおよびカラムレベルのアノテーション |
constraints |
主キー、外部キーなどの参照整合性制約 |
annotationsとconstraintsのデフォルトはfalseです。利用する場合は明示的に有効化します。各属性の仕様はOracle公式のプロファイル属性で確認できます。
保存された属性を確認する
SELECT
profile_name,
attribute_name,
DBMS_LOB.SUBSTR(attribute_value, 4000, 1) AS attribute_value
FROM user_cloud_ai_profile_attributes
WHERE UPPER(profile_name) = 'SALES_ORDER_AI'
AND attribute_name IN ('comments', 'annotations', 'constraints')
ORDER BY attribute_name;
この例では属性値が短いため、目視確認用にDBMS_LOB.SUBSTRを使用しています。一般にATTRIBUTE_VALUEはCLOBなので、大きな属性の完全一致判定へこの表示用SQLを流用しないでください。
SHOWPROMPTでメタデータを確認する
設定後は、まずSHOWPROMPTでSELECT AIが構築するプロンプトを確認します。
SELECT DBMS_CLOUD_AI.GENERATE(
prompt => '2026年の確定受注金額を得意先別に集計して',
profile_name => 'SALES_ORDER_AI',
action => 'showprompt'
) AS augmented_prompt
FROM dual;
確認するポイントは次のとおりです。
- 「得意先」が顧客の同義語であるという説明が含まれているか
-
STATUS_CODEと20=確定の対応が含まれているか -
QUANTITYとUNIT_PRICEの意味・単位が含まれているか -
ORDER_DATEが受注実績の期間条件に使う日付だと分かるか -
CUSTOMER_IDとORDER_IDの主キー・外部キー関係が含まれているか - コメントとアノテーションの内容が矛盾していないか
出力形式は環境によって異なります。以下は実際の出力ではなく、確認対象を示す模式例です。
Table SALES.CUSTOMERS
Comment: 顧客マスタ。顧客は得意先、取引先とも呼ぶ
Annotation BUSINESS_TERMS: 顧客,得意先,取引先
CUSTOMER_ID: 顧客を一意に識別するID
CUSTOMER_NAME: 顧客名。得意先名、取引先名と同義
Table SALES.ORDER_HEADERS
Comment: 受注ヘッダー。1行が1受注
ORDER_DATE: 受注実績を期間集計するときに使用する受注日
STATUS_CODE: 10=受付、20=確定、30=出荷済、90=取消
Foreign key: CUSTOMER_ID -> SALES.CUSTOMERS.CUSTOMER_ID
Table SALES.ORDER_LINES
QUANTITY: 受注数量、単位は個
UNIT_PRICE: 1個あたりの税抜受注単価、通貨は円
Foreign key: ORDER_ID -> SALES.ORDER_HEADERS.ORDER_ID
SHOWPROMPTの出力には、スキーマ名、カラム名、コメント、アノテーションなどが含まれる可能性があります。本番環境の出力を、そのままログや公開記事へ貼り付けないでください。
SHOWSQLで生成結果を確認する
次に、同じ質問をSHOWSQLで確認します。
SELECT DBMS_CLOUD_AI.GENERATE(
prompt => '2026年の確定受注金額を得意先別に集計して',
profile_name => 'SALES_ORDER_AI',
action => 'showsql'
) AS generated_sql
FROM dual;
以下は説明用の想定出力です。モデル、AIプロファイル、メタデータによって実際のSQLは変わります。
SELECT
c.customer_id,
c.customer_name,
SUM(ol.quantity * ol.unit_price) AS confirmed_order_amount
FROM sales.order_headers oh
JOIN sales.order_lines ol
ON ol.order_id = oh.order_id
JOIN sales.customers c
ON c.customer_id = oh.customer_id
WHERE oh.order_date >= DATE '2026-01-01'
AND oh.order_date < DATE '2027-01-01'
AND oh.status_code = '20'
GROUP BY c.customer_id, c.customer_name
ORDER BY confirmed_order_amount DESC
このSQLでは、次を確認します。
- 「得意先」を
CUSTOMERSへ対応づけている -
STATUS_CODE = '20'で確定受注を絞り込んでいる - 受注日に対して2026年の範囲を指定している
- 受注金額を数量×税抜単価で計算している
- 外部キーに対応するカラムでJOINしている
- 顧客名ではなく、顧客IDを基準に集計している
想定どおりのSQLが生成されなかった場合は、長い説明を追加する前に、どの判断材料が不足しているかを分解します。
誤ったテーブルを選ぶ
→ object_listと業務プロファイルを確認
用語やコード値を誤解する
→ コメントとアノテーションを確認
JOIN条件を誤る
→ 主キー・外部キーとconstraints属性を確認
業務ごとの計算ルールを誤る
→ メタデータで表現できる範囲を確認し、必要なら追加指示を検討
業務ごとのSQL生成ルールやadditional_instructionsは、第3回で扱います。
効果を評価する
メタデータを追加した後も、1回の成功だけで効果を判断しません。
1. 評価用の質問を固定する
たとえば、次のような質問を用意します。
- 2026年の確定受注金額を得意先別に集計して
- 2026年9月に受注した取消以外の受注件数を表示して
- 2026年の商品別受注数量を多い順に表示して
- 2026年の顧客ごとの平均受注額を計算して
- 2026年8月と2026年9月の受注金額を比較して
2. 変更する条件をメタデータだけにする
比較時には、provider、モデル、リージョン、object_list、質問文を固定します。
次のように1種類ずつ有効化し、各条件で複数回SHOWSQLを実行すると、どの情報が不足していたかを切り分けやすくなります。
| 評価条件 | comments |
constraints |
annotations |
|---|---|---|---|
| 追加メタデータなし | false |
false |
false |
| コメントのみ | true |
false |
false |
| コメント+制約 | true |
true |
false |
| コメント+制約+アノテーション | true |
true |
true |
データベース上のコメント、アノテーション、制約は削除せず、AIプロファイルの3属性だけを切り替えます。「追加メタデータなし」でも、テーブル名、カラム名、データ型などの基本メタデータは送信されます。
すでにコメントとアノテーションへ同じ意味を書いている場合、アノテーションを追加しても結果が変わらないことがあります。プロンプトが冗長になり、結果が悪化する可能性もあります。それも有効な評価結果です。情報の重複ではなく、必要な判断材料が不足している箇所を探します。
3. SQLを項目別に評価する
以下は評価表の記入例であり、実測結果ではありません。
評価対象の質問:2026年の確定受注金額を得意先別に集計して
| メタデータ | 用語対応 | コード値 | JOIN | 集計式 | 日付条件 | 集計粒度 | 総合判定 |
|---|---|---|---|---|---|---|---|
| 追加メタデータなし | × | × | △ | △ | ○ | × | 不正解 |
| コメントのみ | ○ | ○ | △ | ○ | ○ | ○ | 要確認 |
| コメント+制約 | ○ | ○ | ○ | ○ | ○ | ○ | 正解 |
| コメント+制約+アノテーション | ○ | ○ | ○ | ○ | ○ | ○ | 正解 |
実測結果として公開する場合は、AI provider、モデル、リージョン、プロファイル属性、質問、試行回数、生成SQLを記録します。改善しなかった質問も残すと、メタデータで解決できる問題と、別の対策が必要な問題を切り分けやすくなります。
運用で気をつけること
メタデータもソースコードと同じようにレビューする
コメントやアノテーションは、SQL生成の入力になります。アプリケーションから自動生成して無条件に適用するのではなく、次の流れで管理します。
業務担当者が定義を確認
↓
COMMENT / ANNOTATIONS / 制約DDLをレビュー
↓
検証環境へ適用
↓
データディクショナリから読み戻す
↓
SHOWPROMPTで入力を確認
↓
固定質問でSHOWSQLを回帰テスト
↓
問題がなければ本番へ反映
特にコード値と計算ルールは、データベース担当者だけで決めず、業務上の定義を管理する担当者にも確認してもらいます。
参考:コメント・アノテーション候補の作成をアプリケーションで支援する
テーブルやカラムが多い環境では、すべてのコメントとアノテーションを人手だけで作成すると、大きな工数がかかります。この負担を理由に、メタデータ・エンリッチへ取り組みにくい場合もあります。
一つの考え方として、メタデータの収集と候補DDLの作成をアプリケーションで支援できます。たとえば、次のような処理を行う実装が考えられます。
対象のテーブル・ビューを選択
↓
データディクショナリから構造、既存コメント、主キー・外部キーを取得
↓
必要な場合だけ、選択したカラムの代表値を取得
↓
生成モデルでCOMMENT / ANNOTATIONSの候補DDLを作成
↓
対象オブジェクト、内容、重複、機密情報を人がレビュー
↓
検証環境へ適用し、SHOWPROMPTとSHOWSQLで評価
代表値は必須ではありません。代表値の取得を任意にし、取得件数を0にした場合はサンプルを取得せず、生成要求のサンプル欄も空にする実装が考えられます。この場合、生成モデルへ渡す材料を、テーブル名、カラム名、データ型、既存コメント、主キー・外部キーなどのメタデータと、担当者が入力した業務定義に限定できます。テーブルに格納された実データの値を送らずに、候補作成を支援する構成です。
ただし、実データを送らなければ、物理構造や既存メタデータに書かれていないコード値の意味までは判断できません。たとえば、STATUS_CODE='20'が「確定」であることは推測させず、業務担当者が追加入力するか、承認済みの定義書から与えます。また、実データを送らない場合でも、オブジェクト名や既存コメントなどのメタデータは生成モデルへ送られるため、組織の情報管理方針を確認します。
ここで自動化するのは、あくまで候補の作成です。生成されたDDLを無条件に本番へ適用せず、人による確認、適用対象の制限、検証環境での評価を挟みます。さらに、コメントとアノテーションへ同じ説明を二重生成しないよう、どちらを業務定義の正本にするかをルール化します。
スキーマ変更とメタデータを同期する
カラム追加やコード体系の変更後にコメントが古いままだと、物理構造と説明が食い違います。
少なくとも、次の変更を検知したらメタデータを再確認します。
- テーブル・カラムの追加、削除、名称変更
- 主キー・外部キーの変更
- コード値の追加や意味の変更
- 金額、数量、日付の業務定義変更
- ビュー定義とJOIN経路の変更
アプリケーションでメタデータを管理する場合は、ALL_TAB_COMMENTS、ALL_COL_COMMENTS、ALL_ANNOTATIONS_USAGE、ALL_CONSTRAINTS、ALL_CONS_COLUMNSなどから現在値を取得し、期待する定義との差分を確認します。
機密情報をコメントへ書かない
AIプロファイルの対象オブジェクトに関する名前、カラム、コメントなどのメタデータは、SQL生成のためAI providerへ送信されます。Oracle公式ドキュメントでも、機密性の高いオブジェクト名、カラム名、コメントをobject_listへ含めないよう注意されています。
コメントやアノテーションには、次のような情報を書かないようにします。
- APIキー、パスワード、接続情報
- 個人情報を含む実データのサンプル
- 公開範囲を確認していない社内機密
- セキュリティ設定の詳細
コード値の定義を記述する場合も、そのコード体系を外部のAI providerへ送信してよいか、組織のポリシーを確認します。
メタデータは認可やSQL検証の代わりではない
コメントとアノテーションは、LLMへ意味を伝えるための情報です。アクセス権を制御する機能ではありません。また、外部キーがあっても、LLMが常にその経路だけを使うことを保証するわけではありません。
本番アプリケーションでは、第1回で扱ったobject_list、最小権限のデータベースユーザー、生成SQLの構文解析と許可範囲検証を引き続き組み合わせます。
最後のチェックリスト
- コメントが物理名の日本語訳だけで終わらず、日付の役割や1行の粒度まで説明しているか
- コード値の名称だけでなく、集計時の対象・対象外を評価基準として定義しているか
- コメントとアノテーションに不要な重複や矛盾がなく、業務定義の正本と更新担当者が決まっているか
- 似たカラム名へ依存せず、実態に合う主キー・外部キーでJOIN経路を示しているか
- メタデータ変更後に
SHOWPROMPTと固定質問によるSHOWSQLを再確認したか
まとめ
SELECT AIへ見せるテーブルを絞り込んだ次の段階では、そのテーブルが持つ業務上の意味を整備します。
- コメントへテーブルの役割、カラムの意味、コード値、単位、同義語を書く
- 26aiのアノテーションは、名前付きで管理する必要がある情報へ使い、コメントとの重複を避ける
- 主キー・外部キーで、正しいJOIN経路を構造として定義する
- AIプロファイルの
comments、annotations、constraintsを有効にする -
SHOWPROMPTで渡された情報を確認する -
SHOWSQLで用語、コード値、JOIN、集計式、粒度を評価する - メタデータの変更をレビューし、固定質問で回帰テストする
精度改善のポイントは、LLMへ情報を無制限に増やすことではありません。SQL生成に必要な意味を選び、矛盾のない形でデータベース側へ持たせることです。
次回は、メタデータだけでは表現しにくい業務用語やSQL生成ルールを、**用語辞書、質問への注釈、additional_instructions、SHOWPROMPT**を使って扱います。
参考資料
- Select AIの概念
- Select AIについて
- Select AIの使用例
- DBMS_CLOUD_AIパッケージ
- COMMENT文 - Oracle AI Database SQL言語リファレンス
- annotations_clause - Oracle AI Database SQL言語リファレンス
- ALL_TAB_COMMENTS - Oracle AI Databaseリファレンス
- ALL_ANNOTATIONS_USAGE - Oracle AI Databaseリファレンス
- ALL_CONSTRAINTS - Oracle AI Databaseリファレンス
- Oracle AI Database Select AI Capability Matrix