如何将{In}列表添加到oracle db node js

时间:2017-04-10 15:23:50

标签: node.js

我有这个查询

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 个答案:

答案 0 :(得分: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
   }
 );

答案 1 :(得分:0)

在特殊情况下,IN列表的最大大小是已知的,并且它不会太大,每个潜在列表项使用绑定变量会更容易。对于应用程序不知道的任何值,请绑定null。 SQL将是:

sql = 'select * from foo where id in (:v1, :v2, :v3, :v4)';

然后,如果您只有三个数据项,则绑定:

binds = [30, 60, 90, null];
connection.execute(sql, binds, options, executeCbFunc);

另一种解决方案是使用绑定(用于安全性),但建立确切的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 += ")";  // select first_name, last_name from employees where first_name in (:0, :1, :2)

但是,根据此查询的执行频率以及绑定值的数量变化情况,您最终可能会遇到许多“独特”问题。查询字符串。因此,您可能无法获得执行固定SQL语句所带来的语句缓存优势。 [关于JS,上次我检查时,这个简单的for循环比map / join解决方案更快。]

答案 2 :(得分:0)

请勿使用参数手动连接SQL查询的字符串,这可能会导致SQL注入漏洞。

我已经创建了一个tagged template literal 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)
])

源代码(TypeScript):

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

答案 3 :(得分:0)

我假设您正在与node-oracledb合作,就像我现在正在做的那样。即使尝试了executeMany,我仍然无法使用github issue it says that they don't support it as of 2 years ago功能来解决它​​。

所以这是我的解决方案,无需事先知道元素的数量。您应该相应地包装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