使用 oracledb 在 Node 中进行多个 SQL 查询

Multple SQL queries in Node with oracledb

我是 Node 新手,从 Oracle 读取数据时遇到问题。

我已经设置了所有基本示例,可以发出基本查询、处理结果等。

我遇到的问题是我需要;

  1. 执行一个查询(Q1)
  2. 对于 Q1 结果中的每一项,我需要执行第二个查询 (Q2)
  3. 我需要将 Q1 和 Q2s 的结果组合成一个数组给 return 作为承诺

我正在努力寻找一个可以执行#2 的示例 - 使用与 Q1 相同的连接,对来自 Q1 return 的每个项目多次调用相同的查询。

我的代码如下 - 我首先执行读取,然后遍历存储 connection.execute objects 的结果,然后我通过 Promise.all 行 运行 -我只是输出结果,因为我想在编写逻辑以组合 Q1 和 Q2 的结果之前让它工作。

当我通过 mocha 运行 时,结果不包含任何数据 - 我看到列标题但没有数据。

那么我在这里错过了什么?

// placeholder for the connection
let conn;

// return case list array
var caseList = [];
var queryList = [];

return new Promise((resolve, reject) => {

    // retrieve connection
    oracledb.getConnection({
            user: dbconfig.user,
            password: dbconfig.password,
            connectString: dbconfig.connectString
        }) // the connection is returned as a promise
        .then(connection => {

            console.log('Connected to the DB!');

            // assign connection
            conn = connection;

            // execute statement
            return connection.execute(
                `select caseid, casereference, startdate from caseheader inner join orgobjectlink on caseheader.ownerorgobjectlinkid = orgobjectlink.orgobjectlinkid where orgobjectlink.username = :username`,
                [params.username], {
                    outFormat: oracledb.OBJECT // set the output format to be object
                }
            );
        })
        .then(result => {

            // iterate around rows
            result.rows.forEach(row => {

                var caseObj = {
                    caseID: row.CASEID,
                    reference: row.CASEREFERENCE,
                    dateAssigned: moment(row.STARTDATE).format('YYYY-MM-DD'),
                    username: params.username,
                }
                caseList.push(caseObj);

                console.log(caseObj.caseID)
                queryList.push(conn.execute(`select concernroleid, concernrolename from concernrole inner join caseparticipantrole on concernrole.concernroleid = caseparticipantrole.participantroleid where caseparticipantrole.caseid = :caseID and (caseparticipantrole.typecode = 'PRI' or caseparticipantrole.typecode = 'MEM')`,
                    [caseObj.caseID], {
                        outFormat: oracledb.OBJECT
                    }));

            });

            // build up queries
            return Promise.all(queryList).then(results => {
                console.log(results);

                Promise.resolve(results);
            }, err => {
                console.log(err);
            });
        }).then({
            if(conn){
                console.log("Closing DB connection");
                conn.close();

            }
        }).catch(err => {
            console.log('Error', err);
        });

});

一个问题是 Promise.all().then... 函数没有 return 任何东西(并且不需要额外的 resolve())。排序的方法是构建小的、可测试的、承诺 returning 函数,并单独测试它们

简单的开始,写一个mocha测试连接数据库...

function connect() {
    return oracledb.getConnection({
        user: dbconfig.user,
        password: dbconfig.password,
        connectString: dbconfig.connectString
    });
}

这里有一个可以 运行 数据库上的命令。使用您知道会 return 一些结果的简单查询对此进行测试。

function executeCmd(connection, cmd, params) {
    return connection.execute(cmd, params, { outFormat: oracledb.OBJECT });
}

仅通过这两个(以及一个以上)我们就可以勾勒出一个简单的函数来完成这项工作:连接到数据库,运行一个select,异步处理每个结果,然后断开连接。

function connectAndQuery(username) {
    let connection;
    return connect().then(result => {
        connection = result;
        let cmd = `select caseid, casereference, startdate from caseheader inner join orgobjectlink on caseheader.ownerorgobjectlinkid = orgobjectlink.orgobjectlinkid where orgobjectlink.username = :username`;
        return executeCmd(connection, cmd, [username]);
    }).then(result => {
        let promises = result.rows.map(row => processCaseRow(connection, row, username));
        return Promise.all(promises);
    }).then(result => {
        // result should be an array of caseObj's 
        return connection.close().then(() => result);
    });
}

最后要构建和测试的是一个 promise-returning 函数,它处理来自上面主函数的一行。

我不得不对此采取一些自由,但我认为 objective 是——给定一行代表 "case"——构建一个案例对象,包括 [=31] 的集合=] 可以用caseID查询。 (最后一点是我的想法,但如果你愿意,你可以建立一个单独的集合)

// return a promise that resolves to an object with the following properties...
// caseID, reference, dateAssigned, username, concernedRoles
// get concernedRoles by querying the db
function processCaseRow(connection, row, username) {
    var caseObj = {
        caseID: row.CASEID,
        reference: row.CASEREFERENCE,
        dateAssigned: moment(row.STARTDATE).format('YYYY-MM-DD'),
        username: username
    }
    let cmd = `select concernroleid, concernrolename from concernrole inner join caseparticipantrole on concernrole.concernroleid = caseparticipantrole.participantroleid where caseparticipantrole.caseid = :caseID and (caseparticipantrole.typecode = 'PRI' or caseparticipantrole.typecode = 'MEM')`;
    return executeCmd(connection, cmd, row.CASEID).then(result => {
        caseObj.concernedRole = result
        return caseObj
    })
}

Promise.all 对您不起作用,因为您想要使用单个连接,并且如前所述,无论如何连接一次只能做一件事。要使用 promises 解决这个问题,您必须建立和展开一个 promise 链。我可以给你举个例子,但这很讨厌——最好还是忘记我提到过它。

更好的选择是使用 async/await 进入一个简单的 for 循环。我也可以向您展示这方面的例子,但我认为这是错误的举动。我们称此为逐行提取(a.k.a 慢慢地)。

最适合您的解决方案可能是从第一个查询中获取结果并构建一个数组。然后使用这些选项之一执行第二个查询来处理数组。 https://oracle.github.io/node-oracledb/doc/api.html#sqlwherein

您需要在 select 子句中包括 caseid 列,甚至可能按该列排序,以便 post- 在 Node.js 中简化结果集的处理.

此解决方案有可能大大提高性能和资源利用率,但必须平衡您拥有的数据量、资源等。我也可以向您展示一个示例,但它将需要更长的时间,我想从您那里获得更多信息以确保我们走在正确的道路上。