すべてのプロダクト
Search
ドキュメントセンター

PolarDB:自然言語を SQL に変換 (LLM ベースの NL2SQL)

最終更新日:Jan 10, 2026

SQL に不慣れなユーザーでもデータ分析を行えるように、PolarDB for AI は、自然言語を SQL に変換する独自の AI モデルを提供します。この大規模言語モデル (LLM) ベースの自然言語から SQL への変換 (LLM ベースの NL2SQL) 機能は、組み込みモデルとして利用できます。この機能は、大規模言語モデル (LLM) を使用して、自然言語での質問を正確な SQL 文に直接変換します。これにより、コードを記述することなくデータベースと対話でき、データ分析と開発の効率が大幅に向上します。

説明

この機能はカナリアリリース段階です。この機能をご利用になりたい場合は、チケットを送信して有効化を申請してください。

機能概要

PolarDB for AI は、インテリジェントな翻訳機として機能する NL2SQL 機能を提供します。これは、「上海で最も収益の高い店舗はどれか?」といった自然言語の質問を受け取り、それをデータベースのテーブルスキーマと組み合わせます。その後、LLM がこの入力を分析・理解し、データベースで直接実行可能な SQL 文を生成します。

コアワークフローには、以下のステップが含まれます:

  1. テーブルスキーマインデックスの構築:データベースのテーブルスキーマ、カラム情報、コメント、サンプルデータをベクトル化して「データベースマップ」を作成します。このマップは、LLM がデータを理解するのに役立ちます。

  2. NL2SQL 変換の実行:質問をすると、システムはその質問と「データベースマップ」を LLM に送信します。その後、モデルが対応する SQL 文を生成します。

  3. (オプション) モデルのファインチューニング:汎用モデルが理解できない業界固有の用語 (「顧客トラフィック」など) や特定のクエリパターンに対して、モデルをファインチューニングできます。テンプレートやプロンプトを設定して、継続的に精度を向上させることができます。

注意事項

  • 質問の方法:質問では、条件と関連するエンティティの値を明確に記述してください。条件を最初に置き、次に取得したいカラム値に対応するエンティティを続け、最後に考えられるカラム名を指定するようにしてください。例:

    SELECT '部屋数が1つより多い「家」または「アパート」の物件名は何か?'

    この例では、「部屋数が1つより多い」が条件、「家」と「アパート」がカラム値に対応するエンティティ、「物件名」が考えられるカラム名です。

  • クエリ結果の精度LLM ベースの NL2SQL は LLM に基づく AI モデルであり、そのパフォーマンスは多くの要因に影響されます。クエリ結果が期待どおりであることを確認するために、全体の精度に影響を与える可能性のある以下の要因を考慮してください:

    • テーブルとカラムのコメントの豊富さ:各テーブルとそのカラムにコメントを追加すると、クエリの精度が向上します。

    • 質問とカラムコメントの一致度:質問内のキーワードのセマンティックな意味がカラムコメントに近いほど、クエリ結果は良くなります。

    • 生成される SQL 文の長さ:SQL 文に含まれるカラムが少なく、条件が単純な場合、クエリはより正確になります。

    • SQL 文の論理的な複雑さ:SQL 文が使用する高度な構文が少ないほど、クエリはより正確になります。

操作手順

データテーブルの標準化

NL2SQL が機能するためには、モデルがテーブルの意味 (カラム名が何を表すかなど) を理解する必要があります。したがって、LLM ベースの NL2SQL を使用する前に、頻繁に使用するデータテーブルとそのカラムにコメントを追加してください。

  • テーブルコメント

    LLM ベースの NL2SQL モデルがテーブルの基本情報をよりよく理解するのに役立ちます。これにより、モデルは SQL 文に関与するテーブルを特定しやすくなります。コメントは簡潔にし、注文や在庫など、テーブルの主要な内容を要約するようにしてください。コメントは 10 ワード未満に保ち、過度な説明は避けてください。

  • カラムコメント

    カラムコメントは通常、注文番号、日付、店舗名などの一般的な名詞やフレーズで構成されます。これらのコメントは、カラム名の意味を正確に反映します。カラムコメントにサンプルデータや値のマッピングを追加することもできます。たとえば、isValid という名前のカラムの場合、コメントは 有効かどうか。0: いいえ。1: はい。 のようにすることができます。

説明

元のコメントを変更できない場合は、カスタムのテーブルおよびカラムコメント機能を使用して調整できます。詳細については、このドキュメントの関連セクションをご参照ください。

データ準備

このチュートリアルでは、架空の「アリシャンレストラン」管理システムを例として使用します。NL2SQL 機能の使用方法と、特定のビジネスシナリオに合わせてクエリ精度を最適化するためのモデルのファインチューニング方法をステップバイステップで説明します。

  1. サンプルデータの準備 次の SQL 文を実行して、「アリシャンレストラン」システムの 3 つのコアテーブル (店舗テーブル、メニューテーブル、請求書テーブル) を作成します。

    クリックして詳細な SQL を展開して表示

    -- 店舗テーブル
    CREATE TABLE restaurant_info (
      id INT PRIMARY KEY,
      position VARCHAR(128)
    );
    COMMENT ON TABLE restaurant_info IS '店舗テーブル';
    COMMENT ON COLUMN restaurant_info.id IS '店舗 ID';
    COMMENT ON COLUMN restaurant_info.position IS '店舗の場所';
    
    -- メニューテーブル
    CREATE TABLE menu_info (
      id INT PRIMARY KEY,
      name VARCHAR(64),
      type INT,
      unit_price float8
    );
    COMMENT ON TABLE menu_info IS 'メニューテーブル';
    COMMENT ON COLUMN menu_info.id IS '料理 ID';
    COMMENT ON COLUMN menu_info.name IS '料理名';
    COMMENT ON COLUMN menu_info.type IS '料理の種類';
    COMMENT ON COLUMN menu_info.unit_price IS '料理の単価';
    
    -- 請求書テーブル
    CREATE TABLE bill_info (
      id INT PRIMARY KEY,
      items VARCHAR(512),
      actual_amount INT,
      restaurant_id INT,
      waiter VARCHAR(16),
      diner_count INT,
      pay_time DATE
    );
    COMMENT ON TABLE bill_info IS '請求書テーブル';
    COMMENT ON COLUMN bill_info.id IS '請求書 ID';
    COMMENT ON COLUMN bill_info.items IS '注文された料理';
    COMMENT ON COLUMN bill_info.actual_amount IS '実際の支払額';
    COMMENT ON COLUMN bill_info.restaurant_id IS '食事した店舗';
    COMMENT ON COLUMN bill_info.waiter IS 'ウェイター';
    COMMENT ON COLUMN bill_info.diner_count IS '食事客の数';
    COMMENT ON COLUMN bill_info.pay_time IS '注文時間';
    
    INSERT INTO restaurant_info (id, position) VALUES
    (1, '北京市朝陽区三里屯太古里南区'),
    (2, '上海市黄浦区南京路歩行者天国'),
    (3, '広州市天河区天河城 B ブロック'),
    (4, '深セン市南山区科技園科興科学園'),
    (5, '杭州市西湖区文三路黄龍時代広場'),
    (6, '成都市錦江区春熙路 IFS 国際金融センター'),
    (7, '重慶市渝中区解放碑歩行者天国'),
    (8, '西安市雁塔区大雁塔南広場'),
    (9, '南京市玄武区新街口徳基広場'),
    (10, '武漢市江漢区漢口武商広場'),
    (11, '天津市和平区濱江道商業街'),
    (12, '長沙市岳麓区梅溪湖歩歩高広場'),
    (13, '青島市市南区五四広場万象城'),
    (14, '大連市中山区友好広場百盛'),
    (15, '厦門市思明区中山路歩行者天国'),
    (16, '蘇州市蘇州工業園区金鶏湖時代広場'),
    (17, '寧波市鄞州区天一広場'),
    (18, '無錫市梁溪区南禅寺南長街'),
    (19, '合肥市蜀山区万象城'),
    (20, '南昌市紅谷灘区紅谷中大道万達広場');
    
    INSERT INTO menu_info (id, name, type, unit_price) VALUES
    (1, '宮保鶏丁', 1, 38.0),
    (2, '豚の角煮', 1, 48.0),
    (3, '麻婆豆腐', 1, 22.0),
    (4, 'スズキの蒸し物', 1, 68.0),
    (5, '魚香肉絲', 1, 32.0),
    (6, '酸辣土豆絲', 2, 16.0),
    (7, '手撕きキャベツ', 2, 18.0),
    (8, '空心菜のニンニク炒め', 2, 15.0),
    (9, 'トマトと卵の炒め物', 2, 18.0),
    (10, 'カリフラワーの鉄板焼き', 2, 26.0),
    (11, '水煮牛肉', 1, 58.0),
    (12, '回鍋肉', 1, 36.0),
    (13, '黄牛肉の炒め物', 1, 42.0),
    (14, '辣子鶏', 1, 46.0),
    (15, '東坡肉', 1, 52.0),
    (16, '梅ジュース', 3, 12.0),
    (17, 'ココナッツミルクのサゴ', 3, 14.0),
    (18, 'おしるこ', 3, 10.0),
    (19, 'レモンハニーウォーター', 3, 16.0),
    (20, '白きくらげと蓮の実のスープ', 3, 18.0);
    
    INSERT INTO bill_info (id, items, actual_amount, restaurant_id, waiter, diner_count, pay_time) VALUES
    (1, '宮保鶏丁*1,麻婆豆腐*1,酸辣土豆絲*1', 92, 1, '田中 莉子', 4, '2025-03-01'),
    (2, 'スズキの蒸し物*1,豚の角煮*1,手撕きキャベツ*1', 132, 2, '鈴木 強', 5, '2025-03-02'),
    (3, '魚香肉絲*2,トマトと卵の炒め物*1', 82, 3, '佐藤 奈々', 3, '2025-03-03'),
    (4, '水煮牛肉*1,カリフラワーの鉄板焼き*1,梅ジュース*2', 92, 4, '高橋 陽', 6, '2025-03-04'),
    (5, '回鍋肉*1,空心菜のニンニク炒め*1,ココナッツミルクのサゴ*1', 76, 5, '伊藤 芳', 2, '2025-03-05'),
    (6, '黄牛肉の炒め物*1,辣子鶏*1,手撕きキャベツ*1', 104, 6, '渡辺 敏', 4, '2025-03-06'),
    (7, '東坡肉*1,トマトと卵の炒め物*1,白きくらげと蓮の実のスープ*1', 88, 7, '山本 傑', 3, '2025-03-07'),
    (8, '宮保鶏丁*1,スズキの蒸し物*1', 106, 8, '中村 婷', 2, '2025-03-08'),
    (9, '麻婆豆腐*2,酸辣土豆絲*1,レモンハニーウォーター*1', 70, 9, '小林 浩', 4, '2025-03-09'),
    (10, '豚の角煮*1,水煮牛肉*1,手撕きキャベツ*1', 122, 10, '加藤 麗', 5, '2025-03-10'),
    (11, '魚香肉絲*1,カリフラワーの鉄板焼き*1,おしるこ*1', 68, 11, '吉田 翔', 3, '2025-03-11'),
    (12, '辣子鶏*1,空心菜のニンニク炒め*1,梅ジュース*1', 74, 12, '山田 雪', 4, '2025-03-12'),
    (13, '回鍋肉*1,トマトと卵の炒め物*1,ココナッツミルクのサゴ*1', 68, 13, '佐々木 凱', 2, '2025-03-13'),
    (14, '黄牛肉の炒め物*1,酸辣土豆絲*1', 58, 14, '山口 静', 2, '2025-03-14'),
    (15, '東坡肉*1,麻婆豆腐*1,白きくらげと蓮の実のスープ*1', 90, 15, '松本 雷', 3, '2025-03-15'),
    (16, '宮保鶏丁*1,スズキの蒸し物*1,手撕きキャベツ*1', 124, 16, '井上 超', 6, '2025-03-16'),
    (17, '水煮牛肉*1,トマトと卵の炒め物*1,レモンハニーウォーター*1', 94, 17, '木村 梅', 4, '2025-03-17'),
    (18, '豚の角煮*2,空心菜のニンニク炒め*1', 112, 18, '林 潤', 5, '2025-03-18'),
    (19, '魚香肉絲*1,カリフラワーの鉄板焼き*1,梅ジュース*1', 66, 19, '斎藤 偉', 3, '2025-03-19'),
    (20, '宮保鶏丁*1,回鍋肉*1,ココナッツミルクのサゴ*1', 92, 20, '橋本 楓', 4, '2025-03-20');
  2. AI ノードのデータベースアカウントにビジネステーブルの権限を付与します。

    たとえば、AI ノードに設定したデータベース接続アカウントが polarai_user の場合、restaurant_infomenu_info、および bill_info テーブルの権限を polarai_user に付与します。

    説明

    ビジネス環境でこれを適用する場合、AI ノードに設定されたデータベースアカウントが関連するビジネステーブルに対する権限を持っていることを確認してください。

    -- 権限の付与
    GRANT ALL PRIVILEGES ON TABLE public."restaurant_info" TO polarai_user;
    GRANT ALL PRIVILEGES ON TABLE public."menu_info" TO polarai_user;
    GRANT ALL PRIVILEGES ON TABLE public."bill_info" TO polarai_user;

基本的なクエリ

  1. テーブルスキーマインデックスの作成 次のコマンドを実行して、現在のデータベース内のすべてのテーブルに対して schema_index という名前のテーブルスキーマインデックスを作成します。この操作は、テーブル構造、コメント、その他の情報を収集してベクトル化し、LLM にコンテキストを提供します。

    SELECT polar_ai.ai_BuildSchemaIndex('schema_index');

    このコマンドは、bce632ea-97e9-11ee-bdd2-492f4dfe0918 のようなタスク ID を返します。次のコマンドを使用してタスクのステータスを照会できます。ステータスが finish になると、インデックス構築は完了です。

    SELECT polar_ai.ai_ShowTask('<your_task_id>');
  2. NL2SQL クエリの実行 これで、ai_nl2sql 関数を使用して、自然言語の質問を SQL に変換できます。

    単純なクエリ

    • 質問:「今週の総収益はいくらですか?」

    • 実行:

      SELECT polar_ai.ai_nl2sql('今週の総収益はいくらですか?', 'schema_index');
    • 期待される SQL:

      SELECT SUM(actual_amount) AS total_income FROM bill_info WHERE pay_time BETWEEN '2025-08-25' AND '2025-08-31';

    より複雑なクエリシナリオ

    NL2SQL は、ソート、結合、グループ化、計算を含む複雑な質問を処理できます。

    ユーザーの質問

    NL2SQL によって生成された SQL

    各店舗の収益をソートしてください。

    SELECT restaurant_id, SUM(actual_amount) AS total_income FROM bill_info GROUP BY restaurant_id ORDER BY total_income DESC;

    上海で最も収益の高い店舗はどれですか?

    SELECT b.restaurant_id FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id WHERE r.position LIKE '%Shanghai%' GROUP BY r.position ORDER BY SUM(b.actual_amount) DESC LIMIT 1;

    上海の一人当たりの平均消費額はいくらですか?

    SELECT AVG(bill_info.actual_amount / bill_info.diner_count) AS average_consumption_per_person FROM bill_info WHERE bill_info.restaurant_id IN (SELECT id FROM restaurant_info WHERE position = 'Shanghai');

    今月最も注文された料理のトップ 10 は何ですか?

    SELECT mi.name, SUM(bi.items) as total_orders FROM bill_info bi JOIN menu_info mi ON bi.items = mi.id WHERE bi.pay_time >= '2024-10-01' AND bi.pay_time < '2024-11-01' GROUP BY mi.name ORDER BY total_orders DESC LIMIT 10;

    今月の収益の前月比成長率は何パーセントですか?

    SELECT (SUM(CASE WHEN MONTH(pay_time) = 10 AND YEAR(pay_time) = 2024 THEN actual_amount ELSE 0 END) - SUM(CASE WHEN MONTH(pay_time) = 9 AND YEAR(pay_time) = 2024 THEN actual_amount ELSE 0 END)) / SUM(CASE WHEN MONTH(pay_time) = 9 AND YEAR(pay_time) = 2024 THEN actual_amount ELSE 0 END) * 100 AS growth_percentage FROM bill_info;

    上海で最も顧客トラフィックが多い店舗はどれですか?

    SELECT r.position, COUNT(b.id) AS customer_flow FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id WHERE r.position LIKE '%Shanghai%' GROUP BY r.id ORDER BY customer_flow DESC LIMIT 1;

    これらの例から、NL2SQL がソート、結合、グループ化を含むほとんどの複雑なクエリを正確に処理できることがわかります。しかし、場合によっては、モデルの応答が論理的に正しいものの、期待を完全に満たしていないことに気づくかもしれません。

    たとえば、「上海で最も収益の高い店舗はどれですか?」という質問に対して、モデルは店舗 ID (restaurant_id) を返しますが、実際には店舗の住所や名前が欲しいかもしれません。これは、汎用 LLM がクエリのコアロジックは理解しているものの、結果フォーマットに対する暗黙的な期待を捉えきれていないために発生します。この問題は、質問を調整して「店舗名を返してください」と明示的に述べることで一時的に解決できます。しかし、体系的に精度を向上させ、ビジネス知識を組み込むためのより効果的な方法は、PolarDB for AI が提供するモデルのファインチューニング機能を使用することです。以下のセクションでは、モデルをファインチューニングしてこのような問題を解決する方法を示します。

高度な使用法:モデルのファインチューニングによる精度向上

PolarDB for AI は 4 つの高度なメソッドを提供します。以下の問題に遭遇した場合、対応するメソッドを使用して解決できます。

  • 質問テンプレートの設定:一般的な質問テンプレートを設定して、モデルが特定の知識に基づいて SQL 文を生成できるようにします。

  • 設定テーブルの構築:質問を前処理したり、生成された SQL 文を後処理したりします。

  • カスタムテーブルおよびカラムコメント:元のテーブルまたはカラムのコメントを変更できない場合、テーブルとそのカラムに新しいコメントを追加して、元のコメントをオーバーライドできます。

質問テンプレートの設定

質問テンプレートは特定の知識ドメイン向けに設計されており、モデルが質問をよりよく理解するのに役立ちます。一般的な質問テンプレートを設定し、特定の知識を導入してモデルをガイドできます。これにより、モデルはその知識に基づいて SQL 文を生成できます。

手順

  1. 質問テンプレートの設定

    polar_ai 拡張機能は、自動的に質問テンプレートテーブルを作成します。テーブルスキーマは次のとおりです:

    CREATE TABLE public.polar4ai_nl2sql_pattern (
      id serial NOT NULL COMMENT 'プライマリキー' primary key,
      pattern_question text COMMENT 'テンプレートの質問',
      pattern_description text COMMENT 'テンプレートの説明',
      pattern_sql text COMMENT 'テンプレートの SQL',
      pattern_params text COMMENT 'テンプレートのパラメーター'
    ) ;

    質問テンプレートのカラムの説明

    カラム名

    説明

    テンプレートの質問

    LLM ベースの NL2SQL モデルへの入力として提供される、パラメーター付きの質問。

    テンプレートの質問では、パラメーターは #{XXX} の形式で記述する必要があります。

    テンプレートの説明

    日付、年、組織などの一部のエンティティをパラメーターとして使用する、テンプレートの質問の洗練されたバージョン。これらのエンティティは通常、テーブル内の特定のカラムに対応します。

    テンプレートの説明では、パラメーターは [XXX] の形式で記述する必要があります。パラメーターの順序は、テンプレートの質問の順序と一致している必要があります。

    テンプレートの SQL

    テンプレートの質問に対応する正しい SQL 文。この SQL 文では、テンプレートの質問からのパラメーターを変数として扱う必要があります。

    説明

    テンプレートの質問とテンプレートの SQL のパラメーターは同じである必要はありませんが、関連している必要があります。たとえば、パラメーターは同じプレフィックスを持つ必要があり、それらの値の間には 1 対 1 のマッピングが存在する必要があります。#{category}#{categoryCode} を例にとると、category のパラメーター値が普通商標、特別商標、団体商標の場合、対応する categoryCode の値は 0、1、2 となります。詳細については、以下のをご参照ください。

    テンプレートのパラメーター

    table_nameparam_infoexplanation の 3 つのパラメーターで構成される JSON 文字列。パラメーターは次のように記述されます:

    • table_name string:テンプレート SQL 内のテーブル名。

    • param_info array:テンプレート SQL 内のパラメーターの説明。

      • param_name string:パラメーター名。

      • value array:パラメーターのサンプル値。

        説明
        • パラメーターが有限の列挙値セットに対応する場合、value 配列にすべての可能な値をリストします。

        • 値が単なる例である場合は、2〜4 つの値をリストできます。

        • 相互に対応するパラメーターがある場合は、配列インデックスを使用してそれらをマッピングする必要があります。たとえば、#{category}#{categoryCode} の場合、category の「普通商標」は categoryCode の 1 に対応し、「特別商標」は categoryCode の 2 に対応します。詳細については、以下のをご参照ください。

    • explanation string:追加情報。これには通常、生成される SQL 文の要件 (どの情報を出力するか、特定のフィールドの説明など) が含まれます。

    説明

    テンプレートパラメーターを記入する必要がない場合は、値を以下のいずれかに設定できます:

    • NULL

    • 空の文字列

    • 空のリスト文字列 []

    テンプレートの質問

    テンプレートの説明

    テンプレートの SQL

    テンプレートのパラメーター

    コース名が #{courseName} で、指導状況が #{status} のコースをクエリする

    [コース名] と [指導状況] のコースは何ですか?

    SELECT course_name, course_time, course_location 
    FROM courses 
    WHERE 
    course_name=#{courseName} 
    AND status=#{statusCode}

    [{"table_name":"courses","param_info":[{"param_name":"#{courseName}","value":["数学","物理","化学","英語","歴史","地理","生物","コンピュータサイエンス","美術","音楽","体育","プログラミング","文学","心理学","哲学","経済学","社会学","物理実験","化学実験","生物実験"]},{"param_name": "#{status}", "value": ["未開始","進行中"]},{"param_name": "#{statusCode}","value": [0,1]}], "explanation": "コース名 (course_name)、コース時間 (course_time)、コース場所 (course_location) を出力します。注意:status は定数マッピングタイプです。変数マッピングフィールドは statusCode です。"}]

    #{issueDate} 年にリリース予定で、プロジェクトステータスが #{projectStat} の国家標準をクエリする

    [年] と [プロジェクトステータス] でリリース予定の国家標準は何ですか?

    SELECT DISTINCT planNum, projectCnName, projectStat 
    FROM sy_cd_me_buss_std_gjbzjh 
    WHERE 
    `planNum` IS NOT NULL 
    AND `dataStatus` != 3 
    AND `isValid` = 1 
    AND projectStat=#{projectStat} 
    AND DATE_FORMAT(`issueDate`, '%Y')=#{issueDate}

    [{"table_name":"sy_cd_me_buss_std_gjbzjh","param_info":[{"param_name":"#{issueDate}","value":[2009,2010,2011,2012]},{"param_name":"#{projectStat}","value":["意見募集中","公開済み","レビュー中"]}],"explanation":"標準名 (projectCnName)、計画番号、プロジェクトステータスを出力します。"}]

    商標タイプが #{category} で、国際分類が #{intCls} の商標をクエリする

    [商標タイプ] と [国際分類] の商標は何ですか?

    SELECT DISTINCT tmName, regNo, status 
    FROM sy_cd_me_buss_ip_tdmk_new 
    WHERE 
    dataStatus!=3 
    AND isValid = 1 
    AND category=#{categoryCode} 
    AND intCls=#{intClsCode}

    [{"table_name":"sy_cd_me_buss_ip_tdmk_new","param_info":[{"param_name":"#{intCls}","value":["化学原料","顔料・塗料","日用化学品","燃料・油脂","医薬品"]},{"param_name":"#{category}","value":["普通商標","特別商標","団体商標"]},{"param_name":"#{intClsCode}","value":[1,2,3,4,5]},{"param_name":"#{categoryCode}","value":[0,1,2]}],"explanation":"商標名 (tmName)、出願/登録番号 (regNo)、商標ステータス (status) を出力します。注意:category は定数マッピングタイプです。変数マッピングフィールドは categoryCode です。intCls は定数マッピングタイプです。変数マッピングフィールドは intClsCode です。"}]

    このトピックのシナリオを例として使用し、public.polar4ai_nl2sql_pattern テーブルにテンプレートを挿入します。たとえば、「[場所]で最も収益の高い店舗はどれか」という質問が、店舗の場所を正確に返すようにしたいとします。

    INSERT INTO public.polar4ai_nl2sql_pattern (pattern_question, pattern_description, pattern_sql, pattern_params) VALUES (
      '#{position}で最も収益の高い店舗はどれか', 
      '[場所]で最も収益の高い店舗はどれか', 
      'SELECT r.position FROM bill_info b JOIN restaurant_info r ON b.restaurant_id = r.id WHERE r.position LIKE ''%#{position}%'' ORDER BY b.actual_amount DESC LIMIT 1;', 
      '[{"table_name":"bill_info","param_info":[{"param_name":"#{position}","value":["上海"]}], "explanation": "消費場所"}]'
    );

    ここで、#{position} は任意の場所と一致するスロットです。pattern_sql は、生成されることを期待する標準 SQL です。

  2. 質問テンプレートインデックステーブルの作成

    データベースの命名規則に準拠したカスタムインデックステーブル名を指定できます。このトピックでは、例として pattern_index を使用します。質問テンプレートインデックステーブルを作成する SQL 文は次のとおりです:

    SELECT polar_ai.ai_BuildSchemaIndex('pattern_index', '{"mode": "async", "resource": "pattern"}');
    説明

    質問テンプレートインデックステーブルを作成する前に、質問テンプレートテーブルに少なくとも 1 つのレコードが存在することを確認してください。

    パラメーター

    パラメーター

    必須

    説明

    mode

    はい

    データ書き込みモード。非同期モードを示す async に設定します。

    resource

    はい

    リソースタイプ。質問テンプレート情報がベクトル化されることを示す pattern に設定します。

    このコマンドは、bce632ea-97e9-11ee-bdd2-492f4dfe0918 のようなタスク ID を返します。次のコマンドを使用してタスクのステータスを照会できます。taskStatusfinish になると、インデックス構築は完了です。

    SELECT polar_ai.ai_ShowTask('<your_task_id>');
    説明

    public.polar4ai_nl2sql_pattern テーブルのデータが変更された場合、インデックステーブル pattern_index を再構築する必要があります。

  3. オンラインで質問テンプレートを使用する

    クエリを実行する際、pattern_index_name パラメーターを使用して、先ほど作成したテンプレートインデックスを指定します。

    SELECT polar_ai.ai_nl2sql('上海で最も収益の高い店舗はどれか', 'schema_index', '{"pattern_index_name":"pattern_index"}');

    パラメーター

    • schema_index は、現在のデータベースの取得インデックステーブルの名前です。

    • pattern_index_name は、質問テンプレートインデックステーブルの名前です。

    • options 内に複数のパラメーターを設定して、関連する動作を設定できます:

      パラメーター名

      パラメーターの説明

      値の範囲

      pattern_index_top

      取得する最も類似した質問テンプレートの数。

      有効値:[1,10]。

      デフォルト値は 2 で、現在の質問に対して最も最適なテンプレートの上位 2 つのみが選択されることを意味します。

      pattern_index_threshold

      取得した質問テンプレートが類似しているかどうかを判断するために使用されるしきい値。

      有効値:(0,1]。

      デフォルト値は 0.85 で、ベクトル一致スコアが 0.85 を超える質問テンプレートのみが選択されることを意味します。

設定テーブルの構築

質問を前処理したり、最終的に生成された SQL を後処理したりしたい場合は、設定テーブルを使用できます。

シナリオ

  • シナリオ 1:質問内の確定的な単語 (名前、業界固有の用語、製品名など) を置き換える。

    例えば、Zhang San を含むすべての質問について、Zhang SanZS001 に置き換えます。この場合、What were Zhang San's sales last month?What are Zhang San's total sales this year? のような質問は、LLM が呼び出される前に What were ZS001's sales last month?What are ZS001's total sales this year? に前処理できます。

  • シナリオ 2:特定の単語を含む質問に追加情報を加える。

    たとえば、総売上 を含むすべての質問に、総売上 の計算式 総売上 = SUM(売上) を追加します。LLM が呼び出される前に、設定テーブルを使用してこの情報を追加でき、質問が対応する条件を満たしたときに補足されます。

  • シナリオ 3:特定のテーブルまたはカラムの値をマッピングして置き換える。

    たとえば、student_courses テーブルを含むすべての最終 SQL 文で、カラム値マッピングのフォールバック措置として status = 'Leave'status = 0 に置き換えます。

構文

polar_ai 拡張機能は、自動的に設定テーブルを作成します。テーブルスキーマは次のとおりです:

CREATE TABLE polar_ai.polar4ai_nl2sql_llm_config (
  id SERIAL NOT NULL COMMENT 'プライマリキー' Primary Key,
  is_functional tinyint NOT NULL DEFAULT 't' COMMENT 'アクティブかどうか',
  text_condition text COMMENT 'テキスト条件',
  query_function text COMMENT 'クエリ処理',
  formula_function text COMMENT '数式情報',
  sql_condition text COMMENT 'SQL 条件',
  sql_function text COMMENT 'SQL 処理'
) ;
説明

polar4ai_nl2sql_llm_config テーブルのデータが変更された場合、何もする必要はありません。変更はすぐに有効になります。

パラメーター

カラム名

説明

値の範囲

is_functional

この行の設定がアクティブかどうかを指定します。

設定テーブルが存在する場合、デフォルトですべての NL2SQL 操作で使用されます。使用したくないが削除したくない設定項目がある場合は、is_functional を 0 に設定して設定行を非アクティブ化できます。

  • 1 (デフォルト):アクティブ

  • 0:非アクティブ

  • is_functional=1 の場合、この行の設定はアクティブです。

  • is_functional=0 の場合、この行の設定は非アクティブです。

text_condition

前処理:質問に対してテキスト条件チェックを実行します。

条件が満たされた場合、query_functionformula_function カラムが処理に使用されます。それ以外の場合は使用されません。
  • 3 つの条件演算子がサポートされています:&&||!!。それぞれ ANDORNOT を表します。

  • text_condition が空または空の文字列の場合、すべての質問に一致します。

text_conditionZhang San||Li Si&&!!Wang Wu の場合、質問に Zhang San が含まれるか、または Li Si が含まれて Wang Wu が含まれない場合に条件が満たされます。

例:

  • 質問 What are Zhang San's total sales this year?:条件が満たされています。

  • 質問 What are Li Si's total sales this year?:条件は満たされています。

  • 質問 Li Si と Wang Wu の今年の合計売上はいくらですか?: 条件が満たされていません。

query_function

前処理:質問を処理します。

text_condition が満たされた場合に使用されます。
  • 3 つの処理メソッドがサポートされています:appenddeletereplace。それぞれ末尾への追加削除置換を表します。

  • フォーマットは JSON 文字列である必要があります。

query_function{"append":["one","two"],"delete":["?"],"replace":{"Zhang San":"a","Li Si":"b"}} の場合、text_condition が満たされると、質問の末尾に onetwo が追加され、質問から ? が削除されます。最後に、Zhang Sana に、Li Sib に置き換えられます。

例:

  • 質問 What are Zhang San's total sales this year? は、テキスト条件が満たされると、What are a's total sales this yearonetwo に処理されます。

  • 質問 What are Li Si's total sales this year? は、text_condition が満たされると、What are b's total sales this yearonetwo に処理されます。

formula_function

前処理:特定のビジネスコンセプトに関連する計算式やその他の情報を質問に追加します。

text_condition が満たされた場合に使用されます。

-

formula_function総売上:SUM(売上) の場合、質問内の「総売上」は追加情報として SUM(売上) の数式を使用して処理されます。

sql_condition

後処理:モデルによって生成された SQL に対して条件チェックを実行します。

条件が満たされた場合、sql_function が SQL の処理に使用されます。それ以外の場合は使用されません。

  • 3 つの条件演算子がサポートされています:&&||!!。それぞれ ANDORNOT を表します。

  • sql_condition が空または空の文字列の場合、生成されたすべての SQL 文に一致します。

sql_condition=students||student_courses&&!!courses の場合、SQL にテーブル students またはテーブル student_courses が含まれ、かつテーブル courses が含まれない場合に条件が満たされます。

例:

  • SQL 文 SELECT * FROM student_courses:条件が満たされます。

  • SQL 文 SELECT c.course_name FROM student_courses sc JOIN courses c ON sc.courses_id = c.id;:条件は満たされません。

sql_function

後処理:SQL を処理します。ビジネスロジックで値のマッピングを強制するために使用できます。

sql_condition が満たされた場合に使用されます。

  • replace メソッドのみがサポートされており、置換を表します。

  • フォーマットは JSON 文字列である必要があります。

sql_function={"replace":{"status = 'Leave'":"status = 0","status = 'Present'":"status = 1"}} の場合、sql_condition が満たされたときに、SQL 内の status = 'Leave'status = 0 に、status = 'Present'status = 1 に置き換えられます。

サンプルデータ

is_functional

テキスト条件

クエリ関数

数式関数

SQL 条件

SQL 関数

1

Zhang San||Li Si&&!!Wang Wu

{"append":["one","two"],"delete":["?"],"replace":{"Zhang San":"a","Li Si":"b"}}

1

合計売上: SUM(Sales)

1

students||student_courses&&!!courses

{"replace":{"status = 'Leave'":"status = 0","status = 'Present'":"status = 1"}}

シナリオ例

シナリオ 1:ビジネス用語の説明

LLM は「顧客トラフィック」の意味を理解していないかもしれません。「顧客トラフィック」または「客足」が「食事客の数の合計」であることを通知できます。

INSERT INTO public.polar4ai_nl2sql_llm_config (id, is_functional, text_condition, query_function, formula_function, sql_condition, sql_function) VALUES (
  1,
  1, 
  '顧客トラフィック||客足', -- 質問内のキーワードに一致
  '', 
  '顧客トラフィックまたは客足は、食事客の数の合計として計算されます', -- LLM に提供される説明
  '', 
  ''
);

この設定後、インデックスを再構築する必要はありません。「上海で最も顧客トラフィックが多い店舗はどれか?」と再度質問すると、モデルは diner_count フィールド (食事客の数) に基づいて正しい SQL を生成できます。

結果

  • 質問:「上海で最も顧客トラフィックが多い店舗はどれか?」

  • 実行:

    SELECT polar_ai.ai_nl2sql('上海で最も顧客トラフィックが多い店舗はどれか', 'schema_index', '{"pattern_index_name":"pattern_index"}');
  • 返される期待される SQL:

    SELECT r.position, SUM(b.diner_count) AS total_customer_flow FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id WHERE r.position LIKE '%Shanghai%' GROUP BY r.id ORDER BY total_customer_flow DESC LIMIT 1;

同様に、今月の収益の前月比成長率は何パーセントですか? という質問に対して、「前月比」と「前年比」の計算式を public.polar4ai_nl2sql_llm_config 設定テーブルに入力して、生成される SQL の精度を向上させることもできます。練習として試してみてください。

シナリオ 2:SQL クエリメソッドのヒントを提供

デフォルトでは、モデルは地名に対して完全一致 (=) を使用する可能性があり、クエリが失敗する原因となることがあります。店舗の場所にはあいまい一致 (LIKE) を使用するようにヒントを与えることができます。

INSERT INTO public.polar4ai_nl2sql_llm_config (id, is_functional, text_condition, query_function, formula_function, sql_condition, sql_function) VALUES ( 
  2,
  1, 
  '', -- 空の text_condition はグローバルに適用されることを意味します
  '', 
  '店舗の場所 ''position'' のマッチングにはあいまい検索が必要です', 
  '', 
  ''
);

この設定は、モデルが「店舗の場所」を含むクエリを処理する際に LIKE を優先的に使用するのに役立ちます。

説明

空の text_condition は、設定がグローバルに適用されることを意味します。

結果

  • 質問:「上海の一人当たりの平均消費額はいくらですか?」

  • 実行:

    SELECT polar_ai.ai_nl2sql('上海の一人当たりの平均消費額はいくらですか?', 'schema_index');
  • 戻り値として期待される SQL:

    SELECT AVG(b.actual_amount / b.diner_count) FROM bill_info b JOIN restaurant_info r ON b.restaurant_id = r.id WHERE r.position LIKE '%Shanghai%';

同様に、今月の収益の前月比成長率は何パーセントですか? という質問に対して、「前月比」と「前年比」の計算式を public.polar4ai_nl2sql_llm_config 設定テーブルに入力して、生成される SQL の精度を向上させることもできます。練習として試してみてください。

テーブルカラムコメントのカスタマイズ

データテーブルの標準化プロセス中に、既存のデータテーブルまたはカラムのコメントを変更できないことがわかった場合、polar4ai_nl2sql_table_extra_info テーブルにテーブルとそのカラムの新しいコメントを追加できます。NL2SQL を使用すると、このテーブルのコメントが元のテーブルのコメントをオーバーライドします。

構文

polar_ai 拡張機能は、自動的に polar4ai_nl2sql_table_extra_info テーブルを作成します。テーブルスキーマは次のとおりです:

CREATE TABLE polar_ai.polar4ai_nl2sql_table_extra_info (
  id SERIAL NOT NULL PRIMARY KEY COMMENT 'プライマリキー',
  table_name text COMMENT 'テーブル名',
  table_comment text COMMENT 'テーブルコメント',
  column_name text COMMENT 'カラム名',
  column_comment text COMMENT 'カラムコメント'
);
説明

polar4ai_nl2sql_table_extra_info テーブルのデータが変更された場合、polar4ai_nl2sql_table_extra_info テーブルの変更を有効にするために、テーブルスキーマインデックスを再度作成する必要があります。

  1. カスタムカラムコメント用のテーブルを作成します。

  2. menu_info テーブルの type カラムのコメントを変更します。ここでは、type カラムに追加のオプション説明を追加します:料理の種類 1-肉料理、2-野菜料理、3-デザート

    INSERT INTO public.polar4ai_nl2sql_table_extra_info (table_name, table_comment, column_name, column_comment) VALUES ('menu_info','メニューテーブル','type','料理の種類 1-肉料理、2-野菜料理、3-デザート');
  3. テーブルスキーマインデックスの作成操作を再実行します。

    SELECT polar_ai.ai_BuildSchemaIndex('schema_index_new');

    このコマンドは、bce632ea-97e9-11ee-bdd2-492f4dfe0918 のようなタスク ID を返します。次のコマンドを使用してタスクのステータスを照会できます。taskStatusfinish になると、インデックス構築は完了です。

    SELECT polar_ai.ai_ShowTask('<your_task_id>');

結果

  • 質問:「最も野菜料理を販売している店舗はどれですか?」

  • 実行:

    SELECT polar_ai.ai_nl2sql('最も野菜料理を販売している店舗はどれですか?', 'schema_index_new');
  • 返される期待される SQL:

    SELECT r.position FROM bill_info b, menu_info m, restaurant_info r WHERE b.restaurant_id = r.id AND b.items::jsonb ? m.id::text AND m.type = 2 GROUP BY r.position ORDER BY COUNT(*) DESC LIMIT 1;

出力は、モデルが料理の種類menu_info テーブルの type カラムの値 2 にマッピングしたことを示しています。

付録:関連関数

_ai_nl2sql_alter_token

モデルサービスへのアクセスに必要なノードトークンを設定します。これは、NL2SQL 機能を使用する前に必要な初期化ステップです。

構文

text _ai_nl2sql_alter_token(text token);

パラメーター

パラメーター

説明

token

モデルサービスにアクセスするためのキー。

sk-xxxxxx

SELECT polar_ai._ai_nl2sql_alter_token('sk-xxxxxx');

ai_BuildSchemaIndex

データベーススキーマまたは質問テンプレートのベクトル化されたインデックスを作成します。このインデックスは、LLM がデータ構造を理解するのに役立ち、NL2SQL 機能のコアステップです。これは非同期タスクです。

構文

text ai_BuildSchemaIndex(text name, jsonb text2vecOption default '{"mode": "async", "resource": "schema"}');

パラメーター

パラメーター

説明

name

作成するインデックスの名前。オブジェクトの命名規則に準拠する必要があります。

my_schema_index

text2vecOption

(オプション) ベクトル化プロセスの詳細なオプションを設定するために使用される jsonb オブジェクト。

{"mode": "async", "resource": "schema", "tables_included": "tbl1,tbl2"}

text2vecOption の説明

パラメーター

必須

説明

mode

いいえ

データ書き込みモード。デフォルトは async で、非同期実行を示します。

resource

いいえ

ベクトル化されるリソースタイプ。

  • schema (デフォルト):データテーブルのスキーマ情報をベクトル化します。

  • pattern:質問テンプレート情報をベクトル化します。

tables_included

いいえ

ベクトル化するテーブル名のリストを指定します。複数のテーブル名はコンマ (,) で区切ります。デフォルトは空の文字列 '' で、すべてのテーブルが処理されることを意味します。

to_sample

いいえ

カラム値をサンプリングするかどうかを指定します。カラム数が少ないテーブル (15 未満など) では、サンプリングは生成される SQL の品質を向上させるのに役立ちますが、インデックス構築時間が増加します。

  • 0 (デフォルト):サンプリングなし。

  • 1:サンプリングを実行。

columns_excluded

いいえ

NL2SQL 操作から除外するカラムを指定します。デフォルトは空の文字列 '' で、すべてのカラムが含まれることを意味します。フォーマットは 'table1.col1,table1.col2,table2.col1' です。

pattern_table_name

いいえ

resourcepattern の場合、質問テンプレートテーブルの名前を指定します。デフォルトは polar4ai_nl2sql_pattern です。

戻り値
タスクのステータスを照会するために使用できる、一意の非同期タスク ID (job_id) を返します。

-- スキーマのインデックスを作成
SELECT polar_ai.ai_BuildSchemaIndex('my_schema_index');

-- 指定された 2 つのテーブルのスキーマインデックスを作成
SELECT polar_ai.ai_BuildSchemaIndex('my_schema_index_2', '{"mode": "async", "resource": "schema", "tables_included": "my_table1, my_table2"}');

ai_ShowTask

ai_BuildSchemaIndex などの関数によって作成された非同期タスクの実行ステータスを、タスク ID に基づいて照会します。

構文

text ai_ShowTask(text job_id);

パラメーター

パラメーター

説明

job_id

ai_BuildSchemaIndex などの関数によって返される、非同期タスクの一意の ID。

bce632ea-97e9-11ee-bdd2-492f4dfe0918

戻り値
タスクのステータスを返します:

  • init:タスクは初期化中です。

  • Working:タスクは進行中です。

  • Finish:タスクは正常に完了しました。

SELECT polar_ai.ai_ShowTask('bce632ea-97e9-11ee-bdd2-492f4dfe0918');

ai_nl2sql

自然言語テキストを実行可能な SQL 文に変換します。

構文

text ai_nl2sql(text nl, text basic_index_name, jsonb options default '{}');

パラメーター

パラメーター

説明

nl

変換する自然言語の質問。

今週の総収益はいくらですか?

basic_index_name

使用するスキーマインデックスの名前。事前に ai_BuildSchemaIndex を使用して作成しておく必要があります。

my_schema_index

options

(オプション) SQL 生成の高度なオプションを設定するために使用される jsonb オブジェクト。最適化の有効化や取得戦略の調整など。

{"to_optimize": 1, "basic_index_top": 5}

options の説明

パラメーター

必須

説明

to_optimize

いいえ

生成された SQL を最適化するかどうかを指定します。

  • 0 (デフォルト):最適化なし。

  • 1:最適化を実行。PolarDB for AI は生成された SQL 文を処理して、より最適化します。

basic_index_top

いいえ

取得する最も関連性の高いテーブルの数。有効値は [1, 10] で、デフォルト値は 3 です。クエリが複数のテーブルに関与する場合、この値を増やす (たとえば 4 以上に) ことで取得パフォーマンスを向上させることができます。

basic_index_threshold

いいえ

テーブル取得の関連性しきい値。有効値は (0, 1] で、デフォルト値は 0.1 です。このしきい値を超える関連性スコアを持つテーブルのみが選択されます。

pattern_index_name

いいえ

使用する質問テンプレートインデックスの名前を指定します。

pattern_index_top

いいえ

(pattern_index_name が指定されている場合にのみ有効) 取得する最も関連性の高い質問テンプレートの数。有効値は [1, 10] で、デフォルト値は 2 です。

pattern_index_threshold

いいえ

(pattern_index_name が指定されている場合にのみ有効) 質問テンプレート取得の関連性しきい値。有効値は (0, 1] で、デフォルト値は 0.85 です。

戻り値
生成された実行可能な SQL 文字列。

SELECT polar_ai.ai_nl2sql('今週の総収益はいくらですか?', 'schema_index');

-- 結果
SELECT SUM(actual_amount) AS total_income FROM bill_info WHERE pay_time BETWEEN '2025-08-25' AND '2025-08-31';

ai_nl2chart

SQL クエリを実行し、リクエストに基づいてクエリ結果をチャートに変換し、チャートのダウンロード URL を返します。

説明

この機能は、以下のバージョンで利用可能です:

  • PostgreSQL 16 (マイナーエンジンバージョン 2.0.16.10.11.0 以降)。

  • PostgreSQL 14 (マイナーエンジンバージョン 2.0.14.19.38.0 以降)。

構文

text ai_nl2chart(text sql, text usr_query);

パラメーター

パラメーター

説明

sql

実行する SQL クエリ文。

SELECT r.id, SUM(b.actual_amount) AS total_income FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id GROUP BY r.id

usr_query

ユーザーによるチャートの自然言語での説明。

店舗の収益を表示

戻り値
公開アクセス可能な画像 URL を返します。

SELECT polar_ai.ai_nl2chart(
    $$SELECT r.id, SUM(b.actual_amount) AS total_income FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id GROUP BY r.id$$,
    '店舗の収益を表示'
);

-- 結果
-- http://db4ai-aiengine-cn-hangzhou-dataset-pre.oss-cn-hangzhou.aliyuncs.com/pc-xxx/xxx.png?security-token=...

ai_nl2summary

SQL クエリを実行し、リクエストに基づいてクエリ結果の自然言語での要約を提供します。

説明

この機能は、以下のバージョンで利用可能です:

  • PostgreSQL 16 (マイナーエンジンバージョン 2.0.16.10.11.0 以降)。

  • PostgreSQL 14 (マイナーエンジンバージョン 2.0.14.19.38.0 以降)。

構文

text ai_nl2summary(sql text, usr_query text);

パラメーター

パラメーター

説明

sql

実行する SQL クエリ文。

SELECT r.id, SUM(b.actual_amount) AS total_income FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id GROUP BY r.id

usr_query

ユーザーによる要約の自然言語でのリクエスト。

全店舗の収益を要約する

戻り値
2 つの部分を含むテキストを返します:

  • sql_result text:元の SQL クエリ結果。

  • summary_result text:結果の自然言語での要約。

SELECT polar_ai.ai_nl2summary(
    $$SELECT r.id, SUM(b.actual_amount) AS total_income FROM restaurant_info r JOIN bill_info b ON r.id = b.restaurant_id GROUP BY r.id$$,
    '全店舗の収益を要約する'
);

-- 結果
("[{""total_income"": 68, ""id"": 11}, {""total_income"": 70, ""id"": 9}, {""total_income"": 90, ""id"": 15}, {""total_income"": 66, ""id"": 19}, {""total_income"": 82, ""id"": 3}, {""total_income"": 94, ""id"": 17}, {""total_income"": 76, ""id"": 5}, {""total_income"": 92, ""id"": 4}, {""total_income"": 122, ""id"": 10}, {""total_income"": 104, ""id"": 6}, {""total_income"": 58, ""id"": 14}, {""total_income"": 68, ""id"": 13}, {""total_income"": 132, ""id"": 2}, {""total_income"": 124, ""id"": 16}, {""total_income"": 88, ""id"": 7}, {""total_income"": 74, ""id"": 12}, {""total_income"": 92, ""id"": 20}, {""total_income"": 92, ""id"": 1}, {""total_income"": 112, ""id"": 18}, {""total_income"": 106, ""id"": 8}]",)