Node-oracledb: только последнее значение вставляется в цикл несколько раз - PullRequest
0 голосов
/ 06 января 2020

Я читаю файл 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, хотя это глобальная переменная.

Ответы [ 4 ]

1 голос
/ 06 января 2020

Я буду разделять задачи в разных функциях и заключать их в обещания, вместо этого добавляя обратные вызовы в циклы. код будет выглядеть примерно так

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);
      }
  }

}

Надеюсь, это поможет

0 голосов
/ 07 января 2020

Если MNEFAMCOR уникально, например, как:

CREATE TABLE TFAMCORGDS_TEST (CODFAMCOR NUMBER, MNEFAMCOR VARCHAR2(20) PRIMARY KEY, DATMOD DATE, DATFINACT VARCHAR2(10));

, то другим способом (помимо ответов Дэна) сократить круговые поездки является использование batchErrors режима executeMany(). Это позволит вставлять новые записи при пометке записей, которые были дубликатами:

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");
var dbConfig = require(__dirname + "/dbconfig.js");

async function main() {
  const connection = await oracledb.getConnection(dbConfig);

  const sql = `INSERT INTO TFAMCORGDS_TEST (CODFAMCOR,MNEFAMCOR,DATMOD,DATFINACT) 
               VALUES (SCORGDS.NEXTVAL, :ACCOUNT_FAMILY, SYSDATE, NULL)`;

  const options = {
    batchErrors: true,
    bindDefs: {
      ACCOUNT_FAMILY: { type: oracledb.STRING, maxSize: 20 }
    }
  };

  const result = await connection.executeMany(sql, xlData, options);
  await connection.commit();

  console.log("Result is:", result);

}

main();

Если ваша таблица действительно очень большая, вам может понадобиться несколько раз вызвать executeMany() с подмножествами записей .

Примечание: Oracle 12 ввели столбцы идентификаторов, поэтому сложность использования последовательностей может быть уменьшена:

CREATE TABLE TFAMCORGDS_TEST (CODFAMCOR NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY, MNEFAMCOR VARCHAR2(20) PRIMARY KEY, DATMOD DATE, DATFINACT VARCHAR2(10));

При этом вам не нужно включать SCORGDS.NEXTVAL в INSERT.

0 голосов
/ 06 января 2020

Мне нравится Аши sh Ответ Моди, но я не думаю, что он знал, что драйвер уже поддерживает обещания, поэтому его код можно упростить.

Во-первых, вам не нужно getConnection функция. Функция драйвера getConnection уже возвращает обещание, если вы не передаете обратный вызов, поэтому эта функция ничего не добавляет.

Функция get имеет ту же проблему. Он не нужен, потому что метод драйвера execute уже поддерживает обещания.

Код мог бы выглядеть так:

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);
      }
  }

}

Теперь код прост и безопасен , Вы можете остановиться на этом, если вы работаете всего с несколькими строками (и число не будет расти со временем), а производительность будет хорошей. В противном случае, пожалуйста, продолжайте ...

Текущая реализация делает то, что мы называем построчная или медленная за медленной обработкой. Как разработчик, вы должны стараться избегать чрезмерных сетевых обходов (сеть является худшим видом ввода / вывода). У вас есть два execute звонка в l oop, так что для каждой итерации l oop.

есть два обхода. С Oracle у вас есть много инструментов, которые можно сократить на раунде поездки, чтобы вы могли использовать разные подходы здесь. Например, вы можете посмотреть executeMany: https://oracle.github.io/node-oracledb/doc/api.html# -30-base-round-trip

Однако, в этом случае, я думаю, что самый простой подход, вероятно, будет отправлять операторы в базу данных в виде анонимного блока 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 реализует ту же логику c, которую вы использовали ранее в JavaScript. Если вы запустите тестирование с этим кодом по сравнению с предыдущей версией, вы увидите заметное улучшение производительности (чем больше извлекается строк, тем заметнее будет улучшение).

0 голосов
/ 06 января 2020

Вы должны создать отдельную функцию asyn c для доступа к базе данных. Потому что когда мы выполняем некоторые операции с базой данных, это занимает некоторое время. мы должны ждать до конца sh, чтобы сделать следующий процесс. Так что я не собираюсь помещать здесь весь код. Просто пример.

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

}

ПРИМЕЧАНИЕ: Я использую ключевое слово await для выполнения асинхронной функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...