2021年2月15日月曜日

MLEのJavaScriptを動かしてみる

 JavaScriptは学習中なのですが、Oracle Database 21cのMLE(Multilingual Engine)で動かしてみました。その作業ログです。

サンプル・データセットに含まれるEMP/DEPTを使って、SQLワークショップのSQLコマンドからコードを実行します。検索(SELECT)とファンクション呼び出しだけを試してみました。

最初に実行したコードです。

const oracledb = require("mle-js-oracledb");
const sql = "SELECT * FROM EMP";
let result = oracledb.defaultConnection().execute(sql);
console.log(JSON.stringify(result));

印刷された結果です。

{
    "metaData":[{"name":"EMPNO"},{"name":"ENAME"},{"name":"JOB"},{"name":"MGR"},{"name":"HIREDATE"},{"name":"SAL"},{"name":"COMM"},{"name":"DEPTNO"}],
    "rows":[
        [7839,"中島 亜希子","社長",null,"1981-11-17T00:00:00.000Z",5000,null,10],
        [7698,"伊藤 明子","マネージャー",7839,"1981-05-01T00:00:00.000Z",2850,null,30],
        [7782,"坂本 明","マネージャー",7839,"1981-06-09T00:00:00.000Z",2450,null,10],
        [7566,"関口 晃","マネージャー",7839,"1981-04-02T00:00:00.000Z",2975,null,20],
        [7788,"新井 敦子","アナリスト",7566,"1982-12-09T00:00:00.000Z",3000,null,20],
        [7902,"石橋 敦","アナリスト",7566,"1981-12-03T00:00:00.000Z",3000,null,20],
        [7369,"村田 淳","店員",7902,"1980-12-17T00:00:00.000Z",800,null,20],
        [7499,"村上 綾子","セールス",7698,"1981-02-20T00:00:00.000Z",1600,300,30],
        [7521,"斉藤 大介","セールス",7698,"1981-02-22T00:00:00.000Z",1250,500,30],
        [7654,"高橋 大輔","セールス",7698,"1981-09-28T00:00:00.000Z",1250,1400,30],
        [7844,"金子 恵美","セールス",7698,"1981-09-08T00:00:00.000Z",1500,0,30],
        [7876,"増田 秀樹","店員",7788,"1983-01-12T00:00:00.000Z",1100,null,20],
        [7900,"佐野 英樹","店員",7698,"1981-12-03T00:00:00.000Z",950,null,30],
        [7934,"石原 裕美","店員",7782,"1982-01-23T00:00:00.000Z",1300,null,10]
    ]
}

取得された列のmetaDataと実際の列がrowsという行の配列、そしてその行も配列として列の値が返されています。従業員番号と従業員名だけを印刷するには、以下のようなコードを実行します。

const oracledb = require("mle-js-oracledb");
const sql = "SELECT * FROM EMP";
for (let row of oracledb.defaultConnection().execute(sql).rows)
{
console.log(row[0] + " " + row[1]);
}

以下が実行結果です。

7839 中島 亜希子
7698 伊藤 明子
7782 坂本 明
7566 関口 晃
7788 新井 敦子
7902 石橋 敦
7369 村田 淳
7499 村上 綾子
7521 斉藤 大介
7654 高橋 大輔
7844 金子 恵美
7876 増田 秀樹
7900 佐野 英樹
7934 石原 裕美

従業員番号7839の行だけを取り出すには、以下の指定を加えます。SQLにWHERE句を追加し、バインド変数の指定を行います。executeの第2引数として、バインド変数に割り当てる値を指定します。バインド変数の名前に関係なく、割り当ては出現順序で決まります。

const oracledb = require("mle-js-oracledb");
const sql = "SELECT * FROM EMP where empno = :empno";
for (let row of oracledb.defaultConnection().execute(sql, [7839]).rows)
{
console.log(JSON.stringify(row));
console.log(row[0] + " " + row[1] + " " + row[5]);
}

結果は以下になります。

[7839,"中島 亜希子","社長",null,"1981-11-17T00:00:00.000Z",5000,null,10]
7839 中島 亜希子 5000

給与の列SALをOracleNumber型として取り出してみます。以下では、optionsの指定を追加しています。

const oracledb = require("mle-js-oracledb");
const sql = "SELECT * FROM EMP where empno = :empno";
const options = { fetchInfo: { SAL: { type: oracledb.ORACLE_NUMBER } } };
for (let row of oracledb.defaultConnection().execute(sql, [7839], options).rows)
{
console.log(JSON.stringify(row));
console.log(row[0] + " " + row[1] + " " + row[5]);
}

結果は以下になります。

[7839,"中島 亜希子","社長",null,"1981-11-17T00:00:00.000Z",{"impl":{}},null,10]
7839 中島 亜希子 5000

row[5]としての出力では給与は5000となっていますが、JSON.stringifyをみると{"impl":{}}となっており、オブジェクトとして取り出されていることがわかります。JavaScriptのデータ型に単純にマップできない列は、ここでoptionsとして定義したように、fetchInfoとして、対象の列と、マップするオブジェクトを指定します。

モジュールmle-js-oracledbではなく、Oracle APEXが提供しているapex.connを代わりに使用してみます。

最初に以下のコードを実行します。

const sql = "SELECT * FROM EMP";
let result = apex.conn.execute(sql);
console.log(JSON.stringify(result));

結果は以下になります。

{
    "metaData":[{"name":"EMPNO"},{"name":"ENAME"},{"name":"JOB"},{"name":"MGR"},{"name":"HIREDATE"},{"name":"SAL"},{"name":"COMM"},{"name":"DEPTNO"}],
    "rows":[
        {"EMPNO":7839,"ENAME":"中島 亜希子","JOB":"社長","MGR":null,"HIREDATE":"1981-11-17T00:00:00.000Z","SAL":5000,"COMM":null,"DEPTNO":10},
        {"EMPNO":7698,"ENAME":"伊藤 明子","JOB":"マネージャー","MGR":7839,"HIREDATE":"1981-05-01T00:00:00.000Z","SAL":2850,"COMM":null,"DEPTNO":30},
        {"EMPNO":7782,"ENAME":"坂本 明","JOB":"マネージャー","MGR":7839,"HIREDATE":"1981-06-09T00:00:00.000Z","SAL":2450,"COMM":null,"DEPTNO":10},
        {"EMPNO":7566,"ENAME":"関口 晃","JOB":"マネージャー","MGR":7839,"HIREDATE":"1981-04-02T00:00:00.000Z","SAL":2975,"COMM":null,"DEPTNO":20},
        {"EMPNO":7788,"ENAME":"新井 敦子","JOB":"アナリスト","MGR":7566,"HIREDATE":"1982-12-09T00:00:00.000Z","SAL":3000,"COMM":null,"DEPTNO":20},
        {"EMPNO":7902,"ENAME":"石橋 敦","JOB":"アナリスト","MGR":7566,"HIREDATE":"1981-12-03T00:00:00.000Z","SAL":3000,"COMM":null,"DEPTNO":20},
        {"EMPNO":7369,"ENAME":"村田 淳","JOB":"店員","MGR":7902,"HIREDATE":"1980-12-17T00:00:00.000Z","SAL":800,"COMM":null,"DEPTNO":20},
        {"EMPNO":7499,"ENAME":"村上 綾子","JOB":"セールス","MGR":7698,"HIREDATE":"1981-02-20T00:00:00.000Z","SAL":1600,"COMM":300,"DEPTNO":30},
        {"EMPNO":7521,"ENAME":"斉藤 大介","JOB":"セールス","MGR":7698,"HIREDATE":"1981-02-22T00:00:00.000Z","SAL":1250,"COMM":500,"DEPTNO":30},
        {"EMPNO":7654,"ENAME":"高橋 大輔","JOB":"セールス","MGR":7698,"HIREDATE":"1981-09-28T00:00:00.000Z","SAL":1250,"COMM":1400,"DEPTNO":30},
        {"EMPNO":7844,"ENAME":"金子 恵美","JOB":"セールス","MGR":7698,"HIREDATE":"1981-09-08T00:00:00.000Z","SAL":1500,"COMM":0,"DEPTNO":30},
        {"EMPNO":7876,"ENAME":"増田 秀樹","JOB":"店員","MGR":7788,"HIREDATE":"1983-01-12T00:00:00.000Z","SAL":1100,"COMM":null,"DEPTNO":20},
        {"EMPNO":7900,"ENAME":"佐野 英樹","JOB":"店員","MGR":7698,"HIREDATE":"1981-12-03T00:00:00.000Z","SAL":950,"COMM":null,"DEPTNO":30},
        {"EMPNO":7934,"ENAME":"石原 裕美","JOB":"店員","MGR":7782,"HIREDATE":"1982-01-23T00:00:00.000Z","SAL":1300,"COMM":null,"DEPTNO":10}
    ]
}

apex.conn.executeでは、行が配列ではなくオブジェクトとして返されています。そのため、従業員番号と従業員名の一覧を出力するコードには、以下のように配列のインデックスではなく、列名を使います。

const sql = "SELECT * FROM EMP";
for (let row of oracledb.defaultConnection().execute(sql).rows)
{
console.log(row.EMPNO + " " + row.ENAME);
}

以下が実行結果です。

7839 中島 亜希子
7698 伊藤 明子
7782 坂本 明
7566 関口 晃
7788 新井 敦子
7902 石橋 敦
7369 村田 淳
7499 村上 綾子
7521 斉藤 大介
7654 高橋 大輔
7844 金子 恵美
7876 増田 秀樹
7900 佐野 英樹
7934 石原 裕美

バインド変数の割り当ても、配列ではなくオブジェクトと列名を使います。

const sql = "SELECT * FROM EMP where empno = :empno";
for (let row of apex.conn.execute(sql, { empno: 7839 }).rows)
{
console.log(JSON.stringify(row));
console.log(row.EMPNO + " " + row.ENAME + " " + row.SAL);
}

以下が実行結果です。

{"EMPNO":7839,"ENAME":"中島 亜希子","JOB":"社長","MGR":null,"HIREDATE":"1981-11-17T00:00:00.000Z","SAL":5000,"COMM":null,"DEPTNO":10}
7839 中島 亜希子 5000

型指定は同様に行います。

const sql = "SELECT * FROM EMP where empno = :empno";
const options = { fetchInfo: { SAL: { type: oracledb.ORACLE_NUMBER } } };
for (let row of apex.conn.execute(sql, { empno: 7839 }, options).rows)
{
console.log(JSON.stringify(row));
console.log(row.EMPNO + " " + row.ENAME + " " + row.SAL);
}

以下が実行結果です。

{"EMPNO":7839,"ENAME":"中島 亜希子","JOB":"社長","MGR":null,"HIREDATE":"1981-11-17T00:00:00.000Z","SAL":{"impl":{}},"COMM":null,"DEPTNO":10}
7839 中島 亜希子 5000

ファンクション呼び出しの例は以下です。

const sql = "begin :result := upper(:input); end;";
let result = apex.conn.execute(sql, {
result: { dir: apex.db.BIND_OUT },
input: {
dir: apex.db.BIND_IN,
val: "abc"
}
});
console.log(JSON.stringify(result));
console.log(result.outBinds.result);

以下が実行結果です。

{"outBinds":{"result":"ABC"}}
ABC

簡単ですが、以上です。

SQLコマンドでは確かめられないのですが、JavaScript内でページ・アイテムおよびアプリケーション・アイテムを参照するにはapex.envを使用します。

参照する方法は以下です。

var pageText = apex.env.P1_TEXT;
var appText = apex.env.G_TEXT;

設定する方法は以下です。

apex.env.P1_TEXT = 'page text';
apex.env.G_TEXT = 'app text';

Oracle APEXのアプリケーション作成の一助になれば幸いです。