Oracle APEX 24.2に、AIに問い合わせてデータ・モデルを作成する機能が追加されました。この機能の動作確認を、最近話題のDeepSeek-R1を使って実施してみます。オンラインでAPIを呼び出す代わりに、LM Studioのローカル・サーバーにモデルをロードし、OpenAI互換のChat Completions APIで呼び出します。
Apple Macbook ProのM4 Max/128GBで動作する最大のモデルdeepseek-r1-distill-llama-70bを使います。性能は変わるとは思いますが、10GBメモリ以下で動かせるDeepSeek-R1の蒸留モデルもあるようです。
作業結果について、OpenAI GPT-4oとほとんど同じ回答が得られる印象です。ただし、すごい時間がかかる - これはモデルの問題というより、ローカルの環境が遅いから - また、処理時間での制限のため、エラーが発生して回答が得られないことがあります。Chat Completions APIはAPEX_WEB_SERVICE.MAKE_REST_REQUESTで呼び出していますが、その引数p_transfer_timeoutのデフォルト値が180秒のため、それ以上処理に時間がかかるとタイムアウトが発生するようです。
OpenAIのo1でも同様ですが、推論モデルは回答を生成するまでに比較的長い時間がかかります。また、Oracle APEXではデータベース・サーバーからリクエストを送信するため、ストリーミングを扱うことができません。結果として、問い合わせが難しいとAPIリクエストのタイムアウトが発生する可能性が高くなります。
以下より実際に行った作業を紹介します。
使用したLM Studioのバージョンは0.3.8です。
Discoverのアイコンをクリックして、Model Searchを開き、DeepSeek-R1を検索します。LM Studioを動かしているホストで実行可能なモデルをダウンロードします。
本記事ではmlx-community/DeepSeek-R1-Distill-Llama-70B-8bitを使用することにしました。MシリーズのMacでLM Studioを実行しているため、MLXフォーマットのモデルを優先しています。
以下のメッセージを入力します。
国名、ISO3166-1 Alpha-2コード、首都名、首都の位置の経度と緯度を保存する表をEBAJ_COUNTRIESとして作成してください。日本語で会話してください。
-- create tables
create table ebaj_countries (
id number generated by default on null as identity
constraint ebaj_countries_pk primary key,
country_name varchar2(255) not null,
iso_code varchar2(2) not null
constraint ebaj_countries_iso_code_uk unique,
capital_name varchar2(255),
longitude number(9,6),
latitude number(9,6),
row_version integer not null,
created_on date not null,
created_by varchar2(255) not null,
updated_on date not null,
updated_by varchar2(255) not null )
/
-- triggers
create or replace trigger ebaj_countries_biu
before insert or update
on ebaj_countries
for each row
begin
:new.updated_on := sysdate;
:new.updated_by := coalesce(sys_context('APEX$SESSION','APP_USER'),user);
if inserting then
:new.row_version := 1;
:new.created_on := :new.updated_on;
:new.created_by := :new.updated_by;
elsif updating then
:new.row_version := nvl(:old.row_version, 0) + 1;
end if;
end ebaj_countries_biu;
/
列ISO_CODEを主キーとして、列IDは削除してください。
3分弱考えて、以下の回答が返されました。
-- create tables
create table ebaj_countries (
iso_code varchar2(2) not null
constraint ebaj_countries_pk primary key,
country_name varchar2(255) not null,
capital_name varchar2(255),
longitude number(9,6),
latitude number(9,6),
row_version integer not null,
created_on date not null,
created_by varchar2(255) not null,
updated_on date not null,
updated_by varchar2(255) not null )
/
-- triggers
create or replace trigger ebaj_countries_biu
before insert or update
on ebaj_countries
for each row
begin
:new.updated_on := sysdate;
:new.updated_by := coalesce(sys_context('APEX$SESSION','APP_USER'),user);
if inserting then
:new.row_version := 1;
:new.created_on := :new.updated_on;
:new.created_by := :new.updated_by;
elsif updating then
:new.row_version := nvl(:old.row_version, 0) + 1;
end if;
end ebaj_countries_biu;
/
監査列と列row_versionを削除してください。
-- create tables
create table ebaj_countries (
iso_code varchar2(2) not null
constraint ebaj_countries_pk primary key,
country_name varchar2(255) not null,
capital_name varchar2(255),
longitude number(9,6),
latitude number(9,6) )
/
まず、テーブルには次のカラムが必要です:国名、ISOコード、首都名、経度、緯度。ガイドラインに従い、テーブル名には「ebaj」というプレフィックスを付けます。
主キーは数値型のIDで、デフォルトで生成されるものとします。このカラム名を「id」とし、オートインクリメントにはアイデンティティを使用します。
国名と首都名は文字列なので、varchar2(255)型にします。一方、ISOコードは短い文字列なので、varchar2(2)型が適切です。
経度と緯度は数値型とし、小数点が必要なので精度を9、スケールを6に設定します。
監査用カラムとして、created_on、created_by、updated_on、updated_byを追加します。これらは、データがいつ、誰によって挿入または更新されたかを記録します。
また、行のバージョン管理のためにrow_versionカラムを追加します。これは初期値を1とし、行が更新されるたびにインクリメントされるようにします。
トリガーも作成し、挿入や更新の前にcreatedおよびupdatedのタイムスタンプやユーザーを設定するようにします。これにより、両方のケースに対応できます。
インデックスについては、外部キーのためのものが必要ですが、今回外部キーはないため、もしISOコードが頻繁に検索される場合にはiso_codeにインデックスを追加するのが良いでしょう。
よし、ユーザーからのSQLテーブル変更のリクエストに対応する必要があります。以前、ユーザーはebaj_countriesというテーブルを作成し、IDを主キーとした特定のカラムを含むよう求めていました。しかし、今回はIDカラムを削除し、代わりにISO_CODEを主キーにしたいというリクエストです。
まず、この変更の理由を理解する必要があります。ISO_CODEを主キーとして使用するのは理にかなっています。なぜなら、これは各国を一意に識別する固定値であり、クエリや結合を簡素化できるからです。また、IDカラムを削除するのは、冗長性を減らし、テーブル構造をシンプルに保つためかもしれません。
元のSQLコードを見ると、IDカラムは主キーとして定義されており、アイデンティティシーケンスを使用しています。このカラムを削除するには、IDカラムとその主キー制約を削除し、代わりにISO_CODEを主キーとして設定する必要があります。
次に、ISO_CODEカラムは現在一意制約(UNIQUE)が付いていますが、これを主キーに変更します。このためには、一意制約を主キー制約に変更し、正しくインデックスが設定されていることを確認します。
依存関係も考慮する必要があります。IDが削除されるため、このテーブル内ではIDを参照する外部キーがないので、その点は簡単です。ただし、他のテーブルがebaj_countries.idを参照している場合、その参照をISO_CODEに変更する必要があります。ただし、ユーザーが他のテーブルについて言及していないため、今回はこのテーブルに集中します。
テーブル作成スクリプトを調整し、IDカラムを削除してISO_CODEを主キーに設定します。その後、トリガーを確認します。このトリガーはIDカラムをrow_versionで参照していますが、待てよ……トリガーはテーブル構造に基づく:newと:oldを使用しているので、IDが削除されても問題ありません。row_versionはそのまま残っているので、トリガーは正常に動作するはずです。
次に、SQLコードを以下のように再構築します。IDカラムとその制約を削除し、ISO_CODEの制約を主キーに変更し、その他のカラムはそのままにします。トリガーについては、挿入や更新の前に動作するようになっており、IDカラムに直接依存していないので変更は不要です。
この変更の影響も考慮する必要があります。ISO_CODEが主キーになるため、必ずNOT NULLかつ一意(UNIQUE)である必要があります。これは元々の設定と一致しているので問題ありません。一意制約は主キー制約が代わりに適用されるため削除できます。
最後に、更新されたSQLコードを提示し、変更内容を説明します。IDを削除し、新しい主キーを設定したことを要約し、ユーザーのニーズに合致しているか、さらに調整が必要か確認するためのフォローアップ質問をします。
これで十分だと思います。更新されたコードは、ユーザーの要件を正確に反映するはずです。
最後の指示では、以下の推論をしています。
ユーザーがEBAJ_COUNTRIESというテーブルを作成してほしいと依頼しています。このテーブルには、国名、ISO 3166-1 Alpha-2コード、首都名、そしてその首都の経度と緯度を格納する必要があります。また、会話は日本語で進めてほしいとのことです。
まず、ユーザーの要件をデータモデルに変換する必要があります。Oracleでは、キーワードやカラム名は小文字を使用し、予約語を避けるのがベストプラクティスです。また、各テーブルには主キーを設定する必要があり、できればシーケンスやトリガーを使用したIDカラムを自動生成するのが望ましいです。
まず、IDを主キーとしたテーブルを作成します。IDはOracleのアイデンティティ機能を使用して生成します。この機能は、従来のシーケンスを使用する方法よりも簡単です。その後、国名をvarchar2(255)、ISOコードを2文字なのでvarchar2(2)、首都名もvarchar2(255)として追加します。
次に、経度と緯度については、精度とスケールを設定した数値型を使用します。座標は最大6桁の小数点を含む可能性があるため、number(9,6)として定義します。
さらに、監査用のカラムを追加します:created_on、created_by、updated_on、updated_byです。これらはデータが挿入または更新された日時と操作したユーザーを記録します。また、row_versionカラムを追加して楽観的同時実行制御をサポートします。これにより、複数のユーザーが変更を上書きしないようにできます。
ガイドラインに従い、監査用カラムやrow_versionを自動的に更新するトリガーを作成します。このトリガーは挿入または更新の前に実行され、適切なタイムスタンプとユーザー情報を設定します。
テーブル作成後、インデックスも作成する必要があります。主キーはすでにインデックス化されているため、再度インデックスを作成する必要はありません。ただし、外部キーや頻繁に検索されるカラムがある場合、それらに対するインデックスを作成します。
次に、ユーザーの2回目のメッセージを見ると、ISO_CODEを主キーにしてIDカラムを削除したいとのことです。ISOコードは一意性があり、自然キーとして利用できるため、この変更は合理的です。そのため、テーブルを調整し、IDカラムを削除してISO_CODEを主キーとして設定します。
さらに、3回目のやり取りでは、監査用カラムとrow_versionを削除したいとのリクエストがあります。この場合、これらのカラムを削除し、トリガーもそれらを処理しないように更新する必要があります。
このプロセス全体を通じて、Oracleのベストプラクティスを守る必要があります。適切なデータ型や命名規則、制約を使用し、テーブル作成後にトリガーを設定するなど、正しい順序で作業を進めます。
最後に、各変更後にはSQLを検証し、エラーがないか確認します。また、パフォーマンスやデータの整合性を最適化することにも注意を払います。