Получить экспорт модуля из асинхронной функции - PullRequest
0 голосов
/ 11 января 2019

в модуле async.js У меня есть три SQL-запроса, хранящихся в массиве asyncTasks. В функции async.parallel результат публикуется с console.log (results), и все выглядит хорошо. НО как я могу передать этот результат обратно в main.js, откуда я вызвал этот модуль с помощью var as = async.getExtradata () ;. Я думаю, проблема в том, что когда я хочу вернуть ответ в async.js, функция async.parallel не завершается. Как я могу справиться с этим?

// async.js:
var response = [];

function getExradata(reqq, ress){
    oracledb.getConnection(
        config,
        function (err, connection) {
            if (err) { console.error(err.message); return; }
            var asyncTasks = [];
            var items = ['1234','3215','2306'];

            items.forEach(function(item){
                asyncTasks.push(function(callback){
                    connection.execute(
                        "SELECT * FROM mde.mobile_blgkopf WHERE blg_id = 
                         '"+item+"'",
                        [],
                        {
                            outFormat: oracledb.OBJECT
                        },
                        function (err, result) {
                            if (err) {                                   
                                return;
                            }
                            callback(null, result.rows);
                        });
                });
            });
            async.parallel(asyncTasks,
                function(err, results) {
                    console.log(results);
                    response = results;                       
                });
        });
    return response;
};
module.exports.getExtradata = getExradata;


// main.js:
var async = require(__dirname + '/async.js');
var as = async.getExtradata();

Ответы [ 4 ]

0 голосов
/ 19 января 2019

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

Имеются следующие объекты (вы можете запустить их для воссоздания объектов):

-- Create a parent table and populate it.
create table t1 (
  id1 number not null,
  id2 number not null,
  id3 number not null,
  id4 number not null,
  val1 number,
  constraint t_u1 unique (id1, id2, id3, id4)
);

insert into t1 (id1, id2, id3, id4, val1) values (1, 1, 1, 1, 1);
insert into t1 (id1, id2, id3, id4, val1) values (1, 1, 1, 2, 2);
insert into t1 (id1, id2, id3, id4, val1) values (1, 1, 2, 1, 3);
insert into t1 (id1, id2, id3, id4, val1) values (1, 1, 2, 2, 4);
insert into t1 (id1, id2, id3, id4, val1) values (1, 2, 1, 1, 5);
insert into t1 (id1, id2, id3, id4, val1) values (1, 2, 1, 2, 6);
insert into t1 (id1, id2, id3, id4, val1) values (1, 2, 2, 1, 7);
insert into t1 (id1, id2, id3, id4, val1) values (1, 2, 2, 2, 8);
insert into t1 (id1, id2, id3, id4, val1) values (2, 1, 1, 1, 9);
insert into t1 (id1, id2, id3, id4, val1) values (2, 1, 1, 2, 10);
insert into t1 (id1, id2, id3, id4, val1) values (2, 1, 2, 1, 11);
insert into t1 (id1, id2, id3, id4, val1) values (2, 1, 2, 2, 12);
insert into t1 (id1, id2, id3, id4, val1) values (2, 2, 1, 1, 13);
insert into t1 (id1, id2, id3, id4, val1) values (2, 2, 1, 2, 14);
insert into t1 (id1, id2, id3, id4, val1) values (2, 2, 2, 1, 15);
insert into t1 (id1, id2, id3, id4, val1) values (2, 2, 2, 2, 16);

-- Create a child table and populate it.
create table t2 (
  id1 number not null,
  id2 number not null,
  id3 number not null,
  id4 number not null,
  val1 number(,0),
  val2 number,
  foreign key (id1, id2, id3, id4)
    references t1 (id1, id2, id3, id4)
);

insert into t2
select round(dbms_random.value(1,2)),
  round(dbms_random.value(1,2)),
  round(dbms_random.value(1,2)),
  round(dbms_random.value(1,2)),
  round(dbms_random.value(1,100)),
  round(dbms_random.value(1,100))
from dual
connect by rownum <= 1000;

-- Delete some data from the child table so that not every
-- primary key combination has a match.
delete from t2
where id1 = 1
  and id2 = 2
  and id3 = 2
  and id4 = 1;

delete from t2
where id1 = 2
  and id2 = 2
  and id3 = 2
  and id4 = 1;

-- Create a temp table to demonstrate another way to get
-- the values in SQL.
create global temporary table id_values(
  id1 number,
  id2 number,
  id3 number,
  id4 number
) on commit delete rows;

create or replace package my_pkg
as

type number_aat is table of number
  index by pls_integer;

type varchar2_aat is table of varchar2(256)
  index by pls_integer;

procedure get_vals(
  p_id1_vals    in out number_aat,
  p_id2_vals    in out number_aat,
  p_id3_vals    in out number_aat,
  p_id4_vals    in out number_aat,
  p_parent_uks  out varchar2_aat,
  p_parent_sums out number_aat,
  p_child_uks   out varchar2_aat,
  p_child_sums  out number_aat,
  p_child_avgs  out number_aat
);

end my_pkg;
/

create or replace package body my_pkg
as

procedure get_vals(
  p_id1_vals    in out number_aat,
  p_id2_vals    in out number_aat,
  p_id3_vals    in out number_aat,
  p_id4_vals    in out number_aat,
  p_parent_uks  out varchar2_aat,
  p_parent_sums out number_aat,
  p_child_uks   out varchar2_aat,
  p_child_sums  out number_aat,
  p_child_avgs  out number_aat
)

is

begin

  if not p_id1_vals.count = p_id2_vals.count
    and p_id1_vals.count = p_id3_vals.count
    and p_id1_vals.count = p_id4_vals.count
  then
    raise_application_error(-20001, 'ID arrays must have the same number of elements');
  end if;

  -- Move the ids from the arrays passed into the temporary table
  -- so they can be accessed via SQL
  forall idx in 1 .. p_id1_vals.count
    insert into id_values (id1, id2, id3, id4)
      values (p_id1_vals(idx), p_id2_vals(idx), p_id3_vals(idx), p_id4_vals(idx));

  select id1 || ':' || id2 || ':'  || id3 || ':' || id4 as ids_as_string,
    sum(val1)
  bulk collect into p_parent_uks,
    p_parent_sums 
  from t1
  where (id1, id2, id3, id4) in (
    select id1, id2, id3, id4
    from id_values
  )
  group by id1, 
    id2,
    id3,
    id4;

  select id1 || ':' || id2 || ':'  || id3 || ':' || id4 as ids_as_string,
    sum(val1),
    round(avg(val2))
  bulk collect into p_child_uks,
    p_child_sums,
    p_child_avgs 
  from t2
  where (id1, id2, id3, id4) in (
    select id1, id2, id3, id4
    from id_values
  )
  group by id1,
    id2,
    id3,
    id4;

end get_vals;

end my_pkg;
/

Следующее должно работать ...

const oracledb = require('oracledb');
const config = require('./dbConfig.js');

async function runTest() {
  let conn;

  try {
    // These values would come from an end user
    const id1Vals = [1, 2, 1, 2, 2, 1, 2, 1];
    const id2Vals = [2, 1, 1, 1, 2, 2, 1, 1];
    const id3Vals = [2, 2, 2, 1, 2, 2, 2, 2];
    const id4Vals = [1, 1, 2, 2, 1, 1, 2, 1];

    conn = await oracledb.getConnection(config);

    const result = await conn.execute(
     `begin

        my_pkg.get_vals(
          :id1_vals, 
          :id2_vals, 
          :id3_vals, 
          :id4_vals, 
          :parent_uks, 
          :parent_sums, 
          :child_uks, 
          :child_sums, 
          :child_avgs
        );

      end;`,
      {
        id1_vals: {
          dir: oracledb.BIND_IN,
          type: oracledb.NUMBER,
          val: id1Vals,
          maxArraySize: 100
        },
        id2_vals: {
          dir: oracledb.BIND_IN,
          type: oracledb.NUMBER,
          val: id2Vals,
          maxArraySize: 100
        },
        id3_vals: {
          dir: oracledb.BIND_IN,
          type: oracledb.NUMBER,
          val: id3Vals,
          maxArraySize: 100
        },
        id4_vals: {
          dir: oracledb.BIND_IN,
          type: oracledb.NUMBER,
          val: id4Vals,
          maxArraySize: 100
        },
        parent_uks: {
          dir: oracledb.BIND_OUT,
          type: oracledb.STRING,
          maxArraySize: 100
        },
        parent_sums: {
          dir: oracledb.BIND_OUT,
          type: oracledb.NUMBER,
          maxArraySize: 100
        },
        child_uks: {
          dir: oracledb.BIND_OUT,
          type: oracledb.STRING,
          maxArraySize: 100
        },
        child_sums: {
          dir: oracledb.BIND_OUT,
          type: oracledb.NUMBER,
          maxArraySize: 100
        },
        child_avgs: {
          dir: oracledb.BIND_OUT,
          type: oracledb.NUMBER,
          maxArraySize: 100
        }
      }
    );

    // Now that we have the values in JS we can do what we need to with them.
    console.log(result.outBinds);

    // Create a new object to hold the reformatted results.
    const reformattedResults = {};

    // Ensure that there's on property for each primary key combination requested.
    for (let x = 0; x < id1Vals.length; x += 1) {
      let idAsString = `${id1Vals[x]}:${id2Vals[x]}:${id3Vals[x]}:${id4Vals[x]}`;
      reformattedResults[idAsString] = {};
    }

    // Populate the appropriate property with the correct values from the parent
    // table.
    for (let x = 0; x < result.outBinds.parent_uks.length; x += 1) {
      reformattedResults[result.outBinds.parent_uks[x]].parentSum = result.outBinds.parent_sums[x];
    }

    // Populate the appropriate property with the correct values from the child
    // table.
    for (let x = 0; x < result.outBinds.child_uks.length; x += 1) {
      reformattedResults[result.outBinds.child_uks[x]].childSum = result.outBinds.child_sums[x];
      reformattedResults[result.outBinds.child_uks[x]].childAvg = result.outBinds.child_avgs[x];
    }

    console.log(reformattedResults);
  } catch (err) {
    console.error(err);
  } finally {
    if (conn) {
      try {
        await conn.close();
      } catch (err) {
        console.error(err);
      }
    }
  }
}

runTest();

Я создал отдельные «id out bind», по одному для каждой таблицы, к которой я обращался, поскольку я не всегда мог получить удар. Ваши данные, вероятно, отличаются, поэтому вы можете использовать другой подход (например, объединение с таблицей фактов, чтобы убедиться, что строки существуют).

Как всегда, есть много разных подходов, которые вы можете использовать. Надеюсь, вы начинаете видеть некоторые варианты, которые будут работать для вас.

0 голосов
/ 12 января 2019

Спасибо за ваш великолепный ответ DanMcGhan, вы мне очень помогли. Я очень новичок в Oracle / SQL и на самом деле я вызываю процедуру PL / SQL, данную моей компанией, а не просто оператор SELECT. Мне очень трудно удовлетворить мои потребности. Не знаю, стоит ли мне открывать новую ветку для этого, но я думаю, что контекст важен. Процедура, которая дает мне значения, такова:

BEGIN

    SELECT Nvl(Sum(loat_mg),0)
      INTO v_best_lag
      FROM unitrade.loa
    WHERE loa_at_at_se_se_ag_ag_wg = v_ber
       AND loa_at_at_se_se_ag_ag_no = v_wgr
       AND loa_at_at_se_se_no       = v_agr
       AND loa_at_at_no             = v_art
       AND loat_zen = v_fil;

    SELECT Sum(LOAT_MG * stzu_mg)
      INTO v_best_lagkombi
      from unitrade.stz
      join unitrade.loa
        on LOA_AT_AT_SE_SE_AG_AG_WG = stz_st_at_se_se_ag_ag_wg
       AND LOA_AT_AT_SE_SE_AG_AG_NO = stz_st_at_se_se_ag_ag_no
       AND LOA_AT_AT_SE_SE_NO       = stz_st_at_se_se_no
       AND LOA_AT_AT_NO             = stz_st_at_no
    WHERE stz_at_at_se_se_ag_ag_wg = v_ber
       AND stz_at_at_se_se_ag_ag_no = v_wgr
       AND stz_at_at_se_se_no       = v_agr
       AND stz_at_at_no             = v_art
       AND LOAT_ZEN                 = v_fil;

    SELECT Nvl(Sum(bstd_resmg),0)
      INTO v_off_aek
      FROM bst_bsd_view
    WHERE bsd_at_at_se_se_ag_ag_wg     = v_ber
       AND bsd_at_at_se_se_ag_ag_no     = v_wgr
       AND bsd_at_at_se_se_no     = v_agr
       AND bsd_at_at_no     = v_art
       AND bstd_zen = v_fil;

END;

Как видите, процедура возвращает значения для одного элемента, а идентификатор элемента состоит из пяти значений (ber, wgr, agr, art, fil). Так как я могу изменить эту процедуру, чтобы она давала мне по одной строке на элемент, а значения в виде столбцов? PS: Я прочитал много статей на вашем сайте (jsao.io), и это мне очень помогло в начале моей работы. Но теперь я застрял

0 голосов
/ 17 января 2019

TLDR Драйвер в настоящее время не работает со сложными структурами данных (определяемыми пользователем типами и записями PL / SQL), но он может работать с простыми массивами (число, varchar2 и дата). В вашем случае вы можете отправить массив идентификаторов, для которых вам нужны связанные данные, и заполнить привязки данными. Массив id находится в / вне, потому что нет никакой гарантии, что порядок массивов будет соответствовать тому, что было отправлено (если вы сами не сделаете это).

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

Учитывая следующий пакет:

create or replace package my_pkg
as

type number_aat is table of number
  index by pls_integer;

procedure get_vals(
  p_dept_ids     in out number_aat,
  p_emp_counts   out number_aat,
  p_sum_salaries out number_aat
);

end my_pkg;
/

create or replace package body my_pkg
as

procedure get_vals(
  p_dept_ids     in out number_aat,
  p_emp_counts   out number_aat,
  p_sum_salaries out number_aat
)

is

  -- Use an existing varray type so you do not have to create one
  l_number_list sys.odcinumberlist := sys.odcinumberlist();

begin

  -- Extend the varray to match the number of elements in p_dept_ids
  l_number_list.extend(p_dept_ids.count);

  -- Populate the varray with the values from p_dept_ids
  for x in 1 .. p_dept_ids.count
  loop
    l_number_list(x) := p_dept_ids(x);
  end loop;

  -- Populate the out binds (associative arrays) with the data
  select department_id, 
    count(*), 
    sum(salary)
  bulk collect into p_dept_ids, 
    p_emp_counts, 
    p_sum_salaries
  from employees
  where department_id in (
    select column_value
    from table(l_number_list)
  )
  group by department_id;

end get_vals;

end my_pkg;
/

Должно работать следующее:

const oracledb = require('oracledb');
const config = require('./dbConfig.js');

async function runTest() {
  let conn;

  try {
    const deptIdsFromEndUser = [20, 30, 40, 50, 60, 70, 80];
    conn = await oracledb.getConnection(config);

    const result = await conn.execute(
     `begin

        my_pkg.get_vals(:dept_ids, :emp_counts, :sum_salaries);

      end;`,
      {
        dept_ids: {
          dir: oracledb.BIND_INOUT,
          type: oracledb.NUMBER,
          val: deptIdsFromEndUser,
          maxArraySize: 100
        },
        emp_counts: {
          dir: oracledb.BIND_OUT,
          type: oracledb.NUMBER,
          maxArraySize: 100
        },
        sum_salaries: {
          dir: oracledb.BIND_OUT,
          type: oracledb.NUMBER,
          maxArraySize: 100
        }
      }
    );

    // Now that we have the values in JS we can do what we need to with them.
    console.log(result.outBinds);

    // { dept_ids: [ 20, 30, 40, 50, 60, 70, 80 ],
    //   emp_counts: [ 2, 6, 1, 45, 5, 1, 34 ],
    //   sum_salaries: [ 19000, 24900, 6500, 156400, 28800, 10000, 304500 ] }
  } catch (err) {
    console.error(err);
  } finally {
    if (conn) {
      try {
        await conn.close();
      } catch (err) {
        console.error(err);
      }
    }
  }
}

runTest();
0 голосов
/ 11 января 2019

Имейте в виду, что соединение может делать только одну вещь за один раз. Поэтому, даже если вы используете async.parallel, запросы будут по-прежнему выполняться по одному (ваш код просто скрывает этот факт). Хуже того, вы, скорее всего, будете использовать больше фоновых потоков, чем предполагалось (в основном блокируя их, пока они ожидают завершения передних). Это предотвратит масштабирование вашего приложения и приведет к трудным поискам ошибок. Не используйте async.parallel с node-oracledb таким образом.

Кроме того, вы используете конкатенацию строк со значениями в запросе. Это, вероятно, откроет вам проблемы с внедрением SQL-кода и производительностью. Убедитесь, что вместо этого используются переменные связывания. https://oracle.github.io/node-oracledb/doc/api.html#-18-bind-parameters-for-prepared-statements

Когда речь идет о производительности, часто возникают проблемы с передачей данных и передачей данных. Хотя здесь нельзя избежать передачи данных, вы можете получить данные за один прием.

Вот пример, который использует async / await, переменные связывания и одно и то же обращение.

const oracledb = require('oracledb');
const config = require('./dbConfig.js');

async function runTest() {
  let conn;

  try {
    // These values would come from the user
    const group1Id = 30;
    const group2Id = 50;
    const group3Id = 80;

    conn = await oracledb.getConnection(config);

    // First get the data using a single round trip and bind variables.
    const result = await conn.execute(
     `select * 
      from employees 
      where department_id in (:group1, :group2, :group3)`,
      {
        group1: group1Id,
        group2: group2Id,
        group3: group3Id
      },
      {
        outFormat: oracledb.OBJECT
      }
    );

    // Now that we have ALL the data, we can split it up into the buckets
    // we need. There are lots of different ways you could do this, this
    // is just one example.
    const empsInGroup1 = [];
    const empsInGroup2 = [];
    const empsInGroup3 = [];

    for (let i = 0; i < result.rows.length; i += 1) {
      switch (result.rows[i].DEPARTMENT_ID) {
        case group1Id:
          empsInGroup1.push(result.rows[i]);
          break;
        case group2Id:
          empsInGroup2.push(result.rows[i]);
          break;
        case group3Id:
          empsInGroup3.push(result.rows[i]);
          break;
      }
    }

    console.log(empsInGroup1.length); // 6
    console.log(empsInGroup2.length); // 45
    console.log(empsInGroup3.length); // 34
  } catch (err) {
    console.error(err);
  } finally {
    if (conn) {
      try {
        await conn.close();
      } catch (err) {
        console.error(err);
      }
    }
  }
}

runTest();

Конечно, это было не то, что вы действительно просили. :) Чтобы узнать больше об асинхронной синхронизации в отношении HTTP API, логики базы данных и различных других модулей, ознакомьтесь с этой серией статей по созданию REST API: https://jsao.io/2018/03/creating-a-rest-api-with-node-js-and-oracle-database/

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