👋

Azure Open AI Service で SQL を生成するプロンプト例

2023/05/12に公開

OpenAI で企業内の欲しいデータを見つけたいというご要望をよく耳にするかと思います。データが Word や PDF のようなドキュメント化されているものであれば Azure Cognitive Search といった検索エンジンと組み合わせて、より効率的にデータを探すようなことができますが、相手がデータベースに格納されている数値データの場合はこのアプローチを取ることが難しくなります。

このような場合には、ユーザーとのやり取りを担うフロントアプリを仲介させ、

  1. ユーザーがフロントアプリに、「顧客別売上の上位3を教えて」と問い合わせる
  2. フロントアプリが Azure OpenAI Service に問い合わせ内容を入力し、データベースから結果を抽出する SQL 文を生成させる
  3. フロントアプリが生成された SQL 文をデータベースに対して発行し、結果を受け取る
  4. フロントアプリがユーザーに結果を返す

といった方式が有用です。例えば、Azure のサービスを使用した場合の構成は以下のようになります。

ここでは、このようなユースケースにおいて SQL 文を生成するシステムプロンプト例と、それを使った場合の入力/出力例を記載します。

プロンプト例は以下を前提とします。

  • モデルは gpt-35-turbo もしくは gpt-4

  • データベースは Microsoft SQL Server 互換のもの

  • 表定義とリレーションは以下

    CREATE TABLE customer (
        id int,
        name varchar(50),
        country_id int
    );
    
    CREATE TABLE country (
        id int,
        name varchar(20)
    );
    
    CREATE TABLE sales_data (
        id int,
        customer_id int,
        amount int,
        sales_date date
    );
    

システムプロンプト例

システムプロンプトでは、使用するデータベースの種類 (種類により SQL が異なるため) や表定義、SQL 生成のルールを指示します。

"Microsoft SQL Server の専門家であり、質問内容に対してデータベースを検索するとなる SQL 文のみを回答する。説明はいらない。
回答できない場合は「該当するデータが見つからないため、問い合わせ内容を変更してください。」と返信する。
SQL 文は以下のルールに従う
- WHERE 句で日本語を指定する時は、WHERE A = N'XXX' のように N プレフィックスをつける。

表定義は以下。
"""
customer as 顧客表
id as 顧客ID, name as 顧客名, country_id as 国ID

country as 国表
id as 国ID, name as 国名

sales_data as 売上表
id as 売上ID, customer_id as 顧客ID, amount as 売上金額, sales_date as 売上日
"""
SELECT

入力/出力例

上述のシステムプロンプトを使用した場合の入力/出力例を以下に記載します。なお、GPT の性質上、完全に一意な SQL 文となることは難しいですが、システムプロンプトに指示を追加することでより選択肢を狭めさせることができるため、要件に合わない SQL 文が生成されるようでしたらシステムプロンプトを工夫してみてください。

入力例1
顧客ごとの売り上げ合計を集計して

出力例1
SELECT c.id AS 顧客ID, c.name AS 顧客名, SUM(sd.amount) AS 売上合計
FROM customer c
JOIN sales_data sd ON c.id = sd.customer_id
GROUP BY c.id, c.name;

入力例2
日本の売り上げ合計を集計して

出力例2
SELECT SUM(sd.amount) AS 売上合計
FROM sales_data sd
JOIN customer c ON sd.customer_id = c.id
JOIN country co ON c.country_id = co.id
WHERE co.name = N'日本';

入力例3
2023年1月の売上のランキングを集計して

出力例3
SELECT c.id AS 顧客ID, c.name AS 顧客名, SUM(sd.amount) AS 売上合計
FROM customer c
JOIN sales_data sd ON c.id = sd.customer_id
WHERE sd.sales_date >= '2023-01-01' AND sd.sales_date < '2023-02-01'
GROUP BY c.id, c.name
ORDER BY 売上合計 DESC;

入力例4
県ごとの売り上げ合計を集計して

出力例4
該当するデータが見つからないため、問い合わせ内容を変更してください。

考慮点

  • 上述のシステムプロンプトのように表名や列名に別名をつけなくても、入力内容から判断されます。ただし、別名をつけておいたほうがより正確に判断してくれる傾向にあります。
  • リレーションについては、列名から判断してます。例えば customer 表の id 列と sales_data 表の customer_id にリレーションがあるということをプロンプトで指示しなくても判断してくれます。ただし、より正確にしたい場合にはリレーションも指定すると良いです。
    • ただし、customer 表に id 列、sales_data 表に customer_id 列と a_id 列があり、customer 表の id 列は sales_data 表の a_id 列とリレーションがあると指示したとしても、より名前の近い customer_id 列が選択される傾向にあるようです。こちらは極端な例ではありますが、このような例にあたった場合には列名を変更したデータベースの View を用意して名前の誤判断を避けるような工夫もご検討ください。

Discussion