1

我有这个查询

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) 

那么我怎样才能将列表传递给这个查询呢?

4

4 回答 4

1

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

请注意,它使用需要一些额外往返的对象类型,因此此解决方案更适合早期解决方案不可行的情况。

参考:

于 2017-04-11T02:26:44.210 回答
0

我认为你正在和我一起工作,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
于 2020-11-23T17:04:41.720 回答
0

您是否在单个字符串中有一个以逗号分隔的数字列表,或者您是否有一个数字数组?

下面是一个示例,说明如何使用自定义类型和表函数处理逗号分隔的数字列表。首先,使用 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
   }
 );
于 2017-04-10T18:21:40.263 回答
0

不要手动将 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;
  }
}

源代码(Javascript):

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;
  }
}
于 2019-08-22T17:46:16.300 回答