Вот новый ответ, который, я надеюсь, более точно согласуется с вашим вариантом использования. Я все еще пытаюсь заставить вас работать в сетах, а не в ряд за рядом.
Имеются следующие объекты (вы можете запустить их для воссоздания объектов):
-- 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», по одному для каждой таблицы, к которой я обращался, поскольку я не всегда мог получить удар. Ваши данные, вероятно, отличаются, поэтому вы можете использовать другой подход (например, объединение с таблицей фактов, чтобы убедиться, что строки существуют).
Как всегда, есть много разных подходов, которые вы можете использовать. Надеюсь, вы начинаете видеть некоторые варианты, которые будут работать для вас.