仅在循环中多次插入最后一个值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了仅在循环中多次插入最后一个值相关的知识,希望对你有一定的参考价值。

我正在读取XLSX文件,并将记录插入ORACLE数据库。 XLSX包含以下值

H
H
H
H
JK

但是只有JK插入了5次。下面是使用的代码

var XLSX = require('xlsx')
var workbook = XLSX.readFile('Accounts.xlsx');
var sheet_name_list = workbook.SheetNames;
var xlData = XLSX.utils.sheet_to_json(workbook.Sheets[sheet_name_list[0]]);
var connection;
var oracledb = require('oracledb');
oracledb.autoCommit = true;
var dbConfig = require(__dirname + '/dbconfig.js');
var cnt;


 oracledb.getConnection(
    dbConfig,
     function(err, connection) {
        if (err) throw err;

        for (i in xlData) 
        {

            var act_fam = xlData[i].ACCOUNT_FAMILY;



               connection.execute(
                `SELECT * FROM TFAMCORGDS_TEST WHERE MNEFAMCOR='`+ act_fam + `'`,
                function(err, results) {
                    if (err) throw err;
                    cnt = results.rows.length;

                    if (cnt === 0)
                    {
                         connection.execute(
                `INSERT INTO TFAMCORGDS_TEST (CODFAMCOR,MNEFAMCOR,DATMOD,DATFINACT) VALUES (SCORGDS.NEXTVAL,'`+ act_fam + `',SYSDATE,NULL)`,
                function(err, results) {
                    if (err) throw err;
                    console.log('Rows Inserted: ', results.rowsAffected);
                    //do work on the rows here


                }
            );
                    }
             });







        }
    }
);

而且我也不能在"cnt"函数之外使用变量connection.execute值,尽管它是全局变量。

答案

我将任务划分为不同的功能,并将它们包装在promise中,而不是在循环中放置回调。代码看起来像这样

var XLSX = require("xlsx");
var workbook = XLSX.readFile("Accounts.xlsx");
var sheet_name_list = workbook.SheetNames;
var xlData = XLSX.utils.sheet_to_json(workbook.Sheets[sheet_name_list[0]]);
var connection;
var oracledb = require("oracledb");
oracledb.autoCommit = true;
var dbConfig = require(__dirname + "/dbconfig.js");
var cnt;

function getConnection() {
  return new Promise((resolve, reject) => {
    oracledb.getConnection(dbConfig, function(err, connection) {
      if (err) {
        reject(err);
      } 
      resolve(connection);
    });
  });
}

function get(connection, act_fam) {
  return new Promise((resolve, reject) => {
    connection.execute(`SELECT * FROM TFAMCORGDS_TEST WHERE MNEFAMCOR='` + act_fam + `'`, function(err, results) {
      if (err) {
        reject(err);
      } 
      resolve(results);
    });
  });
}

function insert(connection, act_fam) {
  return new Promise((resolve, reject) => {
    connection.execute(
      `INSERT INTO TFAMCORGDS_TEST (CODFAMCOR,MNEFAMCOR,DATMOD,DATFINACT) VALUES (SCORGDS.NEXTVAL,'` +
        act_fam +
        `',SYSDATE,NULL)`, function(err, results) {
      if (err) {
        reject(err);
      } 
      resolve(results);
    });
  });
}

async function main() {
  const connection = await getConnection();
  for (i in xlData) 
  {
      var act_fam = xlData[i].ACCOUNT_FAMILY;
      const results = await get(connection, act_fam);
      cnt = results.rows.length;
      if (cnt === 0) {
        const insertResult = await insert(connection, act_fam);
        console.log('Rows Inserted: ', insertResult.rowsAffected);
      }
  }

}

希望这会有所帮助

另一答案

您应该创建一个单独的异步函数来访问数据库。因为当我们执行一些数据库操作时需要花费一些时间。我们必须等到完成后再处理下一个。因此,我不会将整个代码放在这里。只是一个例子。

async function main(){

    //send the data to database accessing function
    for(i in xlData){
       await insertData(xlData[i].ACCOUNT_FAMILY);
    }

}


async function insertData(data){

  //do the database stuff here

}

NOTE:我正在使用await关键字执行异步功能。

另一答案

[我喜欢Ashish Modi的答案,但是我不认为他知道驱动程序已经支持promises,因此可以简化他的代码。

首先,您不需要getConnection函数。如果您不传递回调,则驱动程序的getConnection函数已经返回了promise,因此该函数不会添加任何内容。

get功能有相同的问题。不需要,因为驱动程序的execute方法已经支持promises。

代码可能看起来像这样:

var XLSX = require("xlsx");
var workbook = XLSX.readFile("Accounts.xlsx");
var sheet_name_list = workbook.SheetNames;
var xlData = XLSX.utils.sheet_to_json(workbook.Sheets[sheet_name_list[0]]);
var connection;
var oracledb = require("oracledb");
oracledb.autoCommit = true;
var dbConfig = require(__dirname + "/dbconfig.js");

async function main() {
  const connection = await oracledb.getConnection(dbConfig);
  for (i in xlData) 
  {
      var act_fam = xlData[i].ACCOUNT_FAMILY;
      const results = await connection.execute(`SELECT * FROM TFAMCORGDS_TEST WHERE MNEFAMCOR='` + act_fam + `'`);
      var cnt = results.rows.length;
      if (cnt === 0) {
        const insertResult = await connection.execute(
      `INSERT INTO TFAMCORGDS_TEST (CODFAMCOR,MNEFAMCOR,DATMOD,DATFINACT) VALUES (SCORGDS.NEXTVAL,'` +
        act_fam +
        `',SYSDATE,NULL)`);
        console.log('Rows Inserted: ', insertResult.rowsAffected);
      }
  }

}

但是,代码仍然存在很大的问题:SQL注入漏洞和过多的往返行程。

该代码当前正在使用字符串串联将值传递给SQL语句,这将使您面临SQL注入和性能问题。您应该改为使用绑定变量,如此处所述:https://oracle.github.io/node-oracledb/doc/api.html#bind

代码如下所示:

var XLSX = require("xlsx");
var workbook = XLSX.readFile("Accounts.xlsx");
var sheet_name_list = workbook.SheetNames;
var xlData = XLSX.utils.sheet_to_json(workbook.Sheets[sheet_name_list[0]]);
var connection;
var oracledb = require("oracledb");
oracledb.autoCommit = true;
var dbConfig = require(__dirname + "/dbconfig.js");

async function main() {
  const connection = await oracledb.getConnection(dbConfig);
  for (i in xlData) 
  {
      var act_fam = xlData[i].ACCOUNT_FAMILY;
      const results = await connection.execute('SELECT * FROM TFAMCORGDS_TEST WHERE MNEFAMCOR= :act_fam', [act_fam]);
      var cnt = results.rows.length;
      if (cnt === 0) {
        const insertResult = await connection.execute(
      'INSERT INTO TFAMCORGDS_TEST (CODFAMCOR,MNEFAMCOR,DATMOD,DATFINACT) VALUES (SCORGDS.NEXTVAL, :act_fam,SYSDATE,NULL)', [act_fam]);
        console.log('Rows Inserted: ', insertResult.rowsAffected);
      }
  }

}

现在该代码既简单又安全。如果您只处理几行(并且该行数不会随着时间增加),则可以停在那里,并且性能还可以。否则,请继续...

当前实现正在执行我们所谓的逐行或逐个慢速处理。作为开发人员,您应该尝试避免过多的网络往返(网络是最糟糕的I / O类型)。您在循环中有两个execute调用,因此循环的每次迭代都是两次往返。

使用Oracle,您可以使用许多工具来减少往返行程,因此您可以在此处采用不同的方法。例如,您可以查看executeManyhttps://oracle.github.io/node-oracledb/doc/api.html#-30-database-round-trips

但是,在这种情况下,我认为最简单的方法可能是将语句作为匿名PL / SQL块发送到数据库。看起来像这样:

var XLSX = require("xlsx");
var workbook = XLSX.readFile("Accounts.xlsx");
var sheet_name_list = workbook.SheetNames;
var xlData = XLSX.utils.sheet_to_json(workbook.Sheets[sheet_name_list[0]]);
var connection;
var oracledb = require("oracledb");
oracledb.autoCommit = true;
var dbConfig = require(__dirname + "/dbconfig.js");

async function main() {
  const connection = await oracledb.getConnection(dbConfig);
  const act_fams = [];
  for (i in xlData) 
  {
    act_fams.push(xlData[i].ACCOUNT_FAMILY);
  }

  await connection.execute(
   `declare

      type number_aat is table of number
        index by pls_integer;

      l_act_fam_arr number_aat;
      l_count       number;

    begin

      l_act_fam_arr := :act_fam_arr;

      for act_fam in 1 .. l_act_fam_arr.count
      loop
        select count(*) 
        into l_count
        from tfamcorgds_test 
        where mnefamcor=act_fam;

        if l_count = 0
        then
          insert into tfamcorgds_test (
            codfamcor,mnefamcor,datmod,datfinact
          ) values (scorgds.nextval, act_fam,sysdate,null);
        end if;
      end loop;
    end;`,
    {
      act_fam_arr: {
        type: oracledb.NUMBER,
        val: act_fams
      }
    }
  );
}

我尚未测试此代码,因此可能存在语法错误。请注意,我传递给execute的第一个参数是一个很大的代码字符串,即PL / SQL块。第二个参数是绑定变量,它是一个数字数组(我假设ACCOUNT_FAMILY是一个数字,但是如果需要,您可以轻松地将其更改为字符串)。

代码和值将通过一次网络往返发送到数据库。 PL / SQL代码实现了与javascript相同的逻辑。如果您使用此代码与以前的版本进行测试,您应该会看到明显的性能改进(读取的行越多,改进的效果就越明显)。

另一答案

如果MNEFAMCOR是唯一的,例如像:

以上是关于仅在循环中多次插入最后一个值的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的核心数据:仅在 for 循环中保存最后一个对象

c#通过for循环多次向数据库中插入数据。

通过 for 循环和 php 数组进行多次插入(通过 jQuery AJAX 接收)

从循环中多次插入并检查现有字段

仅在每次注销时更新time_out字段到数据库中用户的最后插入ID?

如何在提交按钮上多次单击时仅在数据库中插入一条记录