如何通过节点oracle-db更新或插入数百万行?

时间:2017-01-24 16:33:32

标签: node.js oracle sql-optimization node-oracle oracledb

我正在努力解决一个问题 - 如何使用node oracle-db驱动程序插入或更新大量数据(数千或数百万行)?

关键是我可以在resultSet(handling result set)的帮助下选择大量数据......但是我必须对一行进行一些操作,然后更新或插入一个新行。这就是问题 - 我不知道如何尽快做到这一点。

有人可以帮我提一些建议吗?感谢。

1 个答案:

答案 0 :(得分:1)

  

我可以向您保证,这些操作无法在db中完成。

实际上,有很多不同的方法可以在需要时通过SQL和PL / SQL在DB中完成。人们通常希望使用他们熟悉的语言,在这种情况下可能使用JavaScript,但如果数据不必在层之间飞行,性能会好得多。

这里只是SQL中的一个例子......当然,这可以通过虚拟列来完成,但它应该说明这一点。

想象一下,我们有以下表格:

create table things (
  id   number not null, 
  val1 number not null, 
  val2 number not null, 
  constraint things_pk primary key (id)
);

insert into things (id, val1, val2) values (1, 1, 2);
insert into things (id, val1, val2) values (2, 2, 2);
insert into things (id, val1, val2) values (3, 5, 5);

-- Will hold the sum of things val1 and val2
create table thing_sums (
  thing_id number,
  sum      number
);

alter table thing_sums
add constraint thing_sums_fk1 
foreign key (thing_id)
references things (id);

现在,最简单,最高效的方法是通过SQL:

insert into thing_sums (
  thing_id,
  sum
)
select id, 
  val1 + val2
from things
where id not in (
  select thing_id
  from thing_sums
);

这是另一个只能通过PL / SQL做同样事情的例子,可以提供更多控制。

begin

  -- This cursor for loop will bulk collect (reduces context switching between 
  -- SQL and PL/SQL engines) implictly. 
  for thing_rec in (
    select *
    from things
    where id not in(
      select thing_id
      from thing_sums
    )
  )
  loop
    -- Logic in this loop could be endlessly complex. I'm inserting the values
    -- within the loop but this logic could be modified to store data in arrays
    -- and then insert with forall (another bulk operation) after the loop.
    insert into thing_sums(
      thing_id,
      sum
    ) values (
      thing_rec.id,
      thing_rec.val1 + thing_rec.val2
    );
  end loop;

end;

可以从Node.js驱动程序中调用其中任何一个。但是,我们假设您需要从驱动程序执行此操作(可能您正在摄取数据库中尚未存在的数据)。这是一个示例,演示从使用批量处理而不是逐行操作的驱动程序调用PL / SQL。由于减少了往返次数,因此速度更快。

我从我正在处理的博客文章中删除了这个,所以表格定义有点不同:

create table things (
  id   number not null,
  name varchar2(50),
  constraint things_pk primary key (id)
);

以下是JavaScript:

var oracledb = require('oracledb');
var async = require('async');
var config = require('./dbconfig');
var things = [];
var idx;

function getThings(count) {
  var things = [];

  for (idx = 0; idx < count; idx += 1) {
    things[idx] = {
      id: idx,
      name: "Thing number " + idx
    };
  }

  return things;
}

things = getThings(500);

oracledb.getConnection(config, function(err, conn) {
  var ids = [];
  var names = [];
  var start = Date.now();

  if (err) {throw err;}

  // We need to break up the array of JavaScript objects into arrays that
  // work with node-oracledb bindings.
  for (idx = 0; idx < things.length; idx += 1) {
    ids.push(things[idx].id);
    names.push(things[idx].name);
  }

  conn.execute(
    ` declare
        type number_aat is table of number
          index by pls_integer;
        type varchar2_aat is table of varchar2(50)
          index by pls_integer;

        l_ids   number_aat := :ids;
        l_names varchar2_aat := :names;
      begin
        forall x in l_ids.first .. l_ids.last
          insert into things (id, name) values (l_ids(x), l_names(x));
      end;`,
    {
      ids: {
        type: oracledb.NUMBER,
        dir: oracledb.BIND_IN,
        val: ids
      }, 
      names: {
        type: oracledb.STRING,
        dir: oracledb.BIND_IN,
        val: names
      }
    },
    {
      autoCommit: true
    },
    function(err) {
      if (err) {console.log(err); return;}

      console.log('Success. Inserted ' + things.length + ' rows in ' + (Date.now() - start) + ' ms.');
    }
  );
});

我希望有所帮助! :)