我有这个查询
select * from foo where id in (:ListOfIds)
我多次调用此方法,但每次调用都有不同的值,例如
select * from foo where id in (1,2,5)
select * from foo where id in (3,4,6)
那么我怎样才能将列表传递给这个查询呢?
我有这个查询
select * from foo where id in (:ListOfIds)
我多次调用此方法,但每次调用都有不同的值,例如
select * from foo where id in (1,2,5)
select * from foo where id in (3,4,6)
那么我怎样才能将列表传递给这个查询呢?
在 IN 列表的最大大小已知且不太大的情况下,最好为每个潜在列表项使用一个绑定变量。对于应用不知道的任何值,绑定一个空值。
例如,SQL 语句可以是:
sql = `select * from foo where id in (:v1, :v2, :v3, :v4)`;
然后,如果您只有三个数据项,您将绑定:
binds = [30, 60, 90, null];
const results = await connection.execute(sql, binds);
在频繁调用此查询的应用程序中,您将获得有效使用内部语句缓存的优势,从而提高重复查询的性能。
另一种解决方案是使用绑定(出于安全考虑),但构建准确的 SQL 字符串,例如:
binds = ['Christopher', 'Hazel', 'Samuel'];
sql = `select first_name, last_name from employees where first_name in (`;
for (var i=0; i < binds.length; i++)
sql += (i > 0) ? ", :" + i : ":" + i; sql += ")";
// "sql" becomes "select first_name, last_name from employees where first_name in (:0, :1, :2)"
但是,根据执行此查询的频率以及绑定值的数量的可变性,您最终可能会得到许多“唯一”查询字符串。因此,您可能无法获得执行固定 SQL 语句所带来的语句缓存优势 - 您可能会从缓存中推出其他语句,从而导致重复执行它们效率低下。
关于 JavaScript 本身,上次我检查时,这个简单的for
循环比 map/join 解决方案更快。
对于非常大量的绑定值,您可以尝试以下操作:
const sql = `SELECT first_name, last_name
FROM employees
WHERE first_name IN (SELECT * FROM TABLE(:bv))`;
const inlist = ['Christopher', 'Hazel', 'Samuel']; // a very large list
const binds = { bv: { type: "SYS.ODCIVARCHAR2LIST", val: inlist } };
const result = await connection.execute(sql, binds);
console.dir(result, { depth: null });
请注意,它使用需要一些额外往返的对象类型,因此此解决方案更适合早期解决方案不可行的情况。
参考:
我认为你正在和我一起工作,node-oracledb
就像我现在正在做的那样。即使我尝试过,我也无法使用executeMany功能解决它,但在一个github 问题中,它说他们不支持 2 年前。
所以这是我的解决方案,无需事先知道元素的数量。您应该相应地包装 try catch 并再次关闭连接。我不想膨胀这个例子。阅读他们的文档!
let queries = []
values.forEach(value => {
const query = {
sqlQuery: `SELECT * FROM MY_TABLE where MY_COLUMN = (:value)`,
binds: {
value
}
}
queries.push(query)
})
const connection = await getConnection()
const results = []
for (let i = 0; i < queries.length; i+=1) {
const result = await connection.execute(queries[i].sqlQuery, queries[i].binds)
results.push(result.rows[0])
}
return results
您是否在单个字符串中有一个以逗号分隔的数字列表,或者您是否有一个数字数组?
下面是一个示例,说明如何使用自定义类型和表函数处理逗号分隔的数字列表。首先,使用 SQL Developer 之类的工具在数据库中运行它:
create or replace type number_ntt as table of number;
/
create or replace function string_to_number_ntt(
p_string in varchar2,
p_separator in varchar2 := ','
)
return number_ntt
is
l_elements apex_application_global.vc_arr2;
l_retval number_ntt := number_ntt();
begin
l_elements := apex_util.string_to_table(p_string, p_separator);
for x in 1 .. l_elements.count
loop
l_retval.extend();
l_retval(l_retval.count) := l_elements(x);
end loop;
return l_retval;
end string_to_number_ntt;
/
请注意,该函数使用apex_util
可能已经安装在数据库中的包。
一旦到位,您可以按如下方式使用它:
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
let conn;
oracledb.getConnection(config)
.then((c) => {
conn = c;
return conn.execute(
`select employee_id,
last_name,
department_id
from employees
where department_id in (
select column_value
from table(string_to_number_ntt(:depts))
)`,
{
depts: '30, 60, 90'
},
{
outFormat: oracledb.OBJECT
}
);
})
.then(result => {
console.log(result.rows);
})
.catch(err => {
console.log(err);
})
.then(() => {
if (conn) { // conn assignment worked, must close
return conn.close();
}
})
.catch(err => {
console.log();
});
示例结果:
$ node my-script.js
[ { EMPLOYEE_ID: 114, LAST_NAME: 'Raphaely', DEPARTMENT_ID: 30 },
{ EMPLOYEE_ID: 115, LAST_NAME: 'Khoo', DEPARTMENT_ID: 30 },
{ EMPLOYEE_ID: 116, LAST_NAME: 'Baida', DEPARTMENT_ID: 30 },
{ EMPLOYEE_ID: 117, LAST_NAME: 'Tobias', DEPARTMENT_ID: 30 },
{ EMPLOYEE_ID: 118, LAST_NAME: 'Himuro', DEPARTMENT_ID: 30 },
{ EMPLOYEE_ID: 119, LAST_NAME: 'Colmenares', DEPARTMENT_ID: 30 },
{ EMPLOYEE_ID: 103, LAST_NAME: 'Hunold', DEPARTMENT_ID: 60 },
{ EMPLOYEE_ID: 104, LAST_NAME: 'Ernst', DEPARTMENT_ID: 60 },
{ EMPLOYEE_ID: 105, LAST_NAME: 'Austin', DEPARTMENT_ID: 60 },
{ EMPLOYEE_ID: 106, LAST_NAME: 'Pataballa', DEPARTMENT_ID: 60 },
{ EMPLOYEE_ID: 107, LAST_NAME: 'Lorentz', DEPARTMENT_ID: 60 },
{ EMPLOYEE_ID: 100, LAST_NAME: 'King', DEPARTMENT_ID: 90 },
{ EMPLOYEE_ID: 101, LAST_NAME: 'Kochhar', DEPARTMENT_ID: 90 },
{ EMPLOYEE_ID: 102, LAST_NAME: 'De Haan', DEPARTMENT_ID: 90 } ]
如果您有一个数字数组,您可以将它们转换为以逗号分隔的数字列表,如下所示:
[30, 60, 90].join(',')
这是您的执行语句的示例:
return conn.execute(
`select *
from foo
where id in (
select column_value
from table(string_to_number_ntt(:ListOfIds))
)`,
{
ListOfIds: ListOfIds.join(',')
},
{
outFormat: oracledb.OBJECT
}
);
不要手动将 SQL 查询的字符串与参数连接起来,这可能会导致 SQL 注入漏洞。
我创建了一个标记模板文字 sql
来编写我的 SQL 查询。
const query1 = sql`select * from table where a in (${[1, 2, 3]})`
query1 === {
query: "select * from table where a in (:0, :1, :2)",
parameters:[1,2,3]
}
const query2 = sql`
select *
from table
where name like ${'some str'}
and a in (${[1, 2, 3]})
and b = ${100}
and c in (${['val1', 'val2']})
`
query2 === {
query: "select * from table where name like :0 and a in (:1, :2, :3) and b = :4 and c in (:3, :4)",
parameters: ["some str", 1, 2, 3, 100, "val1", "val2"]
}
const [result1, result2] = await Promise.all([
connection.execute(query1.query, query1.parameters),
connection.execute(query2.query, query2.parameters)
])
interface ParameterizedSQL {
query: string;
parameters: any[];
}
export function sql(queryParts: TemplateStringsArray, ...parameters: any[]): ParameterizedSQL {
if ((queryParts.length - 1) === parameters.length) {
return {
query: queryParts.map((part, index) => index < parameters.length ? `${part}${parameterIndexes(parameters, index)}` : part).join(''),
parameters: parameters.flat(),
};
}
throw new Error("Invalid number of parameters.");
}
function parameterIndexes(parameters: any[], index: number): string {
const newIndex = parameters.slice(0, index).reduce((p, c) => p + (Array.isArray(c) ? c.length : 1), 0);
const parameter = parameters[index];
if (Array.isArray(parameter)) {
const indexes = new Array<number>(parameter.length).fill(index).map((e, i) => e + i);
return ':' + indexes.join(', :');
}
else {
return ':' + newIndex;
}
}
function sql(queryParts, ...parameters) {
if ((queryParts.length - 1) === parameters.length) {
return {
query: queryParts.map((part, index) => index < parameters.length ? `${part}${parameterIndexes(parameters, index)}` : part).join(''),
parameters: parameters.flat(),
};
}
throw new Error("Invalid number of parameters.");
}
function parameterIndexes(parameters, index) {
const newIndex = parameters.slice(0, index).reduce((p, c) => p + (Array.isArray(c) ? c.length : 1), 0);
const parameter = parameters[index];
if (Array.isArray(parameter)) {
const indexes = new Array(parameter.length).fill(index).map((e, i) => e + i);
return ':' + indexes.join(', :');
}
else {
return ':' + newIndex;
}
}