369

IN使用带有 实例的SQL 子句的最佳解决方法是什么java.sql.PreparedStatement,由于 SQL 注入攻击安全问题,不支持多个值:一个?占位符代表一个值,而不是值列表。

考虑以下 SQL 语句:

SELECT my_column FROM my_table where search_column IN (?)

使用本质上是对首先 preparedStatement.setString( 1, "'A', 'B', 'C'" );使用原因的解决方法的非工作尝试。?

有哪些解决方法?

4

31 回答 31

211

此处提供了对各种可用选项的分析,以及每种选项的优缺点。

建议的选项是:

  • Prepare SELECT my_column FROM my_table WHERE search_column = ?,为每个值执行它并在客户端合并结果。只需要一个准备好的语句。缓慢而痛苦。
  • 准备SELECT my_column FROM my_table WHERE search_column IN (?,?,?)并执行它。每个大小的 IN 列表需要一个准备好的语句。快速而明显。
  • 准备SELECT my_column FROM my_table WHERE search_column = ? ; SELECT my_column FROM my_table WHERE search_column = ? ; ...并执行它。[或者用UNION ALL这些分号代替。--ed] 每个大小的 IN 列表需要一个准备好的语句。非常慢,严格来说比 差WHERE search_column IN (?,?,?),所以我不知道为什么博主甚至建议它。
  • 使用存储过程来构造结果集。
  • 准备 N 个不同的 size-of-IN-list 查询;比如说,有 2、10 和 50 个值。要搜索具有 6 个不同值的 IN 列表,请填充 size-10 查询,使其看起来像SELECT my_column FROM my_table WHERE search_column IN (1,2,3,4,5,6,6,6,6,6). 任何体面的服务器都会在运行查询之前优化重复值。

这些选项都不是理想的。

如果您使用 JDBC4 和支持的服务器,最好的选择是按照此处所述x = ANY(y)使用PreparedStatement.setArray

不过,似乎没有任何方法可以setArray使用 IN 列表。


有时 SQL 语句在运行时加载(例如,从属性文件),但需要可变数量的参数。在这种情况下,首先定义查询:

query=SELECT * FROM table t WHERE t.column IN (?)

接下来,加载查询。然后在运行之前确定参数的数量。知道参数计数后,运行:

sql = any( sql, count );

例如:

/**
 * Converts a SQL statement containing exactly one IN clause to an IN clause
 * using multiple comma-delimited parameters.
 *
 * @param sql The SQL statement string with one IN clause.
 * @param params The number of parameters the SQL statement requires.
 * @return The SQL statement with (?) replaced with multiple parameter
 * placeholders.
 */
public static String any(String sql, final int params) {
    // Create a comma-delimited list based on the number of parameters.
    final StringBuilder sb = new StringBuilder(
        String.join(", ", Collections.nCopies(possibleValue.size(), "?")));

    // For more than 1 parameter, replace the single parameter with
    // multiple parameter placeholders.
    if (sb.length() > 1) {
        sql = sql.replace("(?)", "(" + sb + ")");
    }

    // Return the modified comma-delimited list of parameters.
    return sql;
}

对于某些不支持通过 JDBC 4 规范传递数组的数据库,该方法可以方便地将 slow子句条件= ?转换为 fastIN (?)子句条件,然后可以通过调用该any方法进行扩展。

于 2008-10-09T22:13:14.000 回答
133

PostgreSQL 的解决方案:

final PreparedStatement statement = connection.prepareStatement(
        "SELECT my_column FROM my_table where search_column = ANY (?)"
);
final String[] values = getValues();
statement.setArray(1, connection.createArrayOf("text", values));

try (ResultSet rs = statement.executeQuery()) {
    while(rs.next()) {
        // do some...
    }
}

或者

final PreparedStatement statement = connection.prepareStatement(
        "SELECT my_column FROM my_table " + 
        "where search_column IN (SELECT * FROM unnest(?))"
);
final String[] values = getValues();
statement.setArray(1, connection.createArrayOf("text", values));

try (ResultSet rs = statement.executeQuery()) {
    while(rs.next()) {
        // do some...
    }
}
于 2012-04-20T04:32:39.820 回答
20

没有简单的方法AFAIK。如果目标是保持较高的语句缓存率(即不为每个参数计数创建一个语句),您可以执行以下操作:

  1. 创建一个带有几个(例如 10 个)参数的语句:

    ... WHERE A IN (?,?,?,?,?,?,?,?,?,?) ...

  2. 绑定所有实际参数

    setString(1,"foo"); setString(2,"bar");

  3. 将其余部分绑定为 NULL

    setNull(3,Types.VARCHAR) ... setNull(10,Types.VARCHAR)

NULL 从不匹配任何内容,因此它会被 SQL 计划生成器优化。

当您将 List 传递给 DAO 函数时,逻辑很容易自动化:

while( i < param.size() ) {
  ps.setString(i+1,param.get(i));
  i++;
}

while( i < MAX_PARAMS ) {
  ps.setNull(i+1,Types.VARCHAR);
  i++;
}
于 2008-10-09T21:52:49.640 回答
13

您可以使用Collections.nCopies来生成占位符集合并使用以下方法加入它们String.join

List<String> params = getParams();
String placeHolders = String.join(",", Collections.nCopies(params.size(), "?"));
String sql = "select * from your_table where some_column in (" + placeHolders + ")";
try (   Connection connection = getConnection();
        PreparedStatement ps = connection.prepareStatement(sql)) {
    int i = 1;
    for (String param : params) {
        ps.setString(i++, param);
    }
    /*
     * Execute query/do stuff
     */
}
于 2018-04-29T09:33:40.570 回答
11

一个令人不快但肯定可行的解决方法是使用嵌套查询。创建一个包含一列的临时表 MYVALUES。将您的值列表插入到 MYVALUES 表中。然后执行

select my_column from my_table where search_column in ( SELECT value FROM MYVALUES )

丑陋,但如果您的值列表非常大,这是一个可行的选择。

如果您的数据库不缓存准备好的语句,这种技术具有额外的优势,即来自优化器的可能更好的查询计划(检查一个页面的多个值,表扫描一次而不是每个值一次等)可以节省开销。您的“插入”需要批量完成,并且可能需要调整 MYVALUES 表以具有最小的锁定或其他高开销保护。

于 2008-10-07T23:49:31.420 回答
9

in() 运算符的局限性是万恶之源。

它适用于琐碎的情况,您可以使用“自动生成准备好的语句”来扩展它,但它总是有其局限性。

  • 如果您正在创建具有可变数量参数的语句,这将在每次调用时产生 sql 解析开销
  • 在许多平台上,in() 运算符的参数数量是有限的
  • 在所有平台上,总 SQL 文本大小是有限的,因此无法为 in 参数发送 2000 个占位符
  • 无法发送 1000-10k 的绑定变量,因为 JDBC 驱动程序有其局限性

in() 方法在某些情况下已经足够好了,但不是火箭证明:)

火箭证明解决方案是在单独的调用中传递任意数量的参数(例如,通过传递一组参数),然后有一个视图(或任何其他方式)在 SQL 中表示它们并在你的 where 中使用标准。

蛮力变体在这里http://tkyte.blogspot.hu/2006/06/varying-in-lists.html

但是,如果您可以使用 PL/SQL,那么这种混乱会变得非常整洁。

function getCustomers(in_customerIdList clob) return sys_refcursor is 
begin
    aux_in_list.parse(in_customerIdList);
    open res for
        select * 
        from   customer c,
               in_list v
        where  c.customer_id=v.token;
    return res;
end;

然后您可以在参数中传递任意数量的逗号分隔的客户 ID,并且:

  • 不会有解析延迟,因为 select 的 SQL 是稳定的
  • 没有流水线功能的复杂性——它只是一个查询
  • SQL 使用简单的连接,而不是 IN 运算符,这非常快
  • 毕竟,使用任何普通的 select 或 DML 访问数据库是一个很好的经验法则,因为它是 Oracle,它提供的光年比 MySQL 或类似的简单数据库引擎还要多。PL/SQL 允许您以有效的方式从应用程序域模型中隐藏存储模型。

这里的诀窍是:

  • 我们需要一个接受长字符串的调用,并将其存储在 db session 可以访问它的位置(例如简单的包变量或 dbms_session.set_context)
  • 然后我们需要一个可以将其解析为行的视图
  • 然后你有一个包含你正在查询的 id 的视图,所以你只需要一个简单的连接到所查询的表。

视图如下所示:

create or replace view in_list
as
select
    trim( substr (txt,
          instr (txt, ',', 1, level  ) + 1,
          instr (txt, ',', 1, level+1)
             - instr (txt, ',', 1, level) -1 ) ) as token
    from (select ','||aux_in_list.getpayload||',' txt from dual)
connect by level <= length(aux_in_list.getpayload)-length(replace(aux_in_list.getpayload,',',''))+1

其中 aux_in_list.getpayload 指的是原始输入字符串。


一种可能的方法是传递 pl/sql 数组(仅由 Oracle 支持),但是您不能在纯 SQL 中使用这些数组,因此始终需要转换步骤。转换不能在 SQL 中完成,所以毕竟,传递一个带有字符串中所有参数的 clob 并在视图中转换它是最有效的解决方案。

于 2016-02-17T14:44:18.823 回答
7

这是我在自己的应用程序中解决它的方法。理想情况下,您应该使用 StringBuilder 而不是对字符串使用 +。

    String inParenthesis = "(?";
    for(int i = 1;i < myList.size();i++) {
      inParenthesis += ", ?";
    }
    inParenthesis += ")";

    try(PreparedStatement statement = SQLite.connection.prepareStatement(
        String.format("UPDATE table SET value='WINNER' WHERE startTime=? AND name=? AND traderIdx=? AND someValue IN %s", inParenthesis))) {
      int x = 1;
      statement.setLong(x++, race.startTime);
      statement.setString(x++, race.name);
      statement.setInt(x++, traderIdx);

      for(String str : race.betFair.winners) {
        statement.setString(x++, str);
      }

      int effected = statement.executeUpdate();
    }

如果您决定稍后更改查询,则使用上面 x 之类的变量而不是具体数字会很有帮助。

于 2016-04-08T05:06:57.013 回答
5

我从来没有尝试过,但是 .setArray() 会做你想要的吗?

更新:显然不是。setArray 似乎仅适用于来自您从先前查询中检索到的 ARRAY 列或具有 ARRAY 列的子查询的 java.sql.Array。

于 2008-10-07T13:45:29.267 回答
5

我的解决方法是:

create or replace type split_tbl as table of varchar(32767);
/

create or replace function split
(
  p_list varchar2,
  p_del varchar2 := ','
) return split_tbl pipelined
is
  l_idx    pls_integer;
  l_list    varchar2(32767) := p_list;
  l_value    varchar2(32767);
begin
  loop
    l_idx := instr(l_list,p_del);
    if l_idx > 0 then
      pipe row(substr(l_list,1,l_idx-1));
      l_list := substr(l_list,l_idx+length(p_del));
    else
      pipe row(l_list);
      exit;
    end if;
  end loop;
  return;
end split;
/

现在您可以使用一个变量来获取表中的一些值:

select * from table(split('one,two,three'))
  one
  two
  three

select * from TABLE1 where COL1 in (select * from table(split('value1,value2')))
  value1 AAA
  value2 BBB

因此,准备好的语句可以是:

  "select * from TABLE where COL in (select * from table(split(?)))"

问候,

哈维尔·伊巴内斯

于 2011-02-24T12:44:47.207 回答
3

我想您可以(使用基本的字符串操作)在 中生成查询字符串,PreparedStatement以使?' 的数量与列表中的项目数相匹配。

当然,如果你这样做,你离在查询中生成一个巨大的链接只有一步之遥OR,但是在查询字符串中没有正确的数量?,我不知道你还能如何解决这个问题。

于 2008-10-07T13:47:36.063 回答
3

您可以使用此 javadoc中提到的 setArray 方法:

PreparedStatement statement = connection.prepareStatement("Select * from emp where field in (?)");
Array array = statement.getConnection().createArrayOf("VARCHAR", new Object[]{"E1", "E2","E3"});
statement.setArray(1, array);
ResultSet rs = statement.executeQuery();
于 2016-06-09T19:34:41.323 回答
2

这是一个完整的 Java 解决方案,可以为您创建准备好的语句:

/*usage:

Util u = new Util(500); //500 items per bracket. 
String sqlBefore  = "select * from myTable where (";
List<Integer> values = new ArrayList<Integer>(Arrays.asList(1,2,4,5)); 
string sqlAfter = ") and foo = 'bar'"; 

PreparedStatement ps = u.prepareStatements(sqlBefore, values, sqlAfter, connection, "someId");
*/



import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Util {

    private int numValuesInClause;

    public Util(int numValuesInClause) {
        super();
        this.numValuesInClause = numValuesInClause;
    }

    public int getNumValuesInClause() {
        return numValuesInClause;
    }

    public void setNumValuesInClause(int numValuesInClause) {
        this.numValuesInClause = numValuesInClause;
    }

    /** Split a given list into a list of lists for the given size of numValuesInClause*/
    public List<List<Integer>> splitList(
            List<Integer> values) {


        List<List<Integer>> newList = new ArrayList<List<Integer>>(); 
        while (values.size() > numValuesInClause) {
            List<Integer> sublist = values.subList(0,numValuesInClause);
            List<Integer> values2 = values.subList(numValuesInClause, values.size());   
            values = values2; 

            newList.add( sublist);
        }
        newList.add(values);

        return newList;
    }

    /**
     * Generates a series of split out in clause statements. 
     * @param sqlBefore ""select * from dual where ("
     * @param values [1,2,3,4,5,6,7,8,9,10]
     * @param "sqlAfter ) and id = 5"
     * @return "select * from dual where (id in (1,2,3) or id in (4,5,6) or id in (7,8,9) or id in (10)"
     */
    public String genInClauseSql(String sqlBefore, List<Integer> values,
            String sqlAfter, String identifier) 
    {
        List<List<Integer>> newLists = splitList(values);
        String stmt = sqlBefore;

        /* now generate the in clause for each list */
        int j = 0; /* keep track of list:newLists index */
        for (List<Integer> list : newLists) {
            stmt = stmt + identifier +" in (";
            StringBuilder innerBuilder = new StringBuilder();

            for (int i = 0; i < list.size(); i++) {
                innerBuilder.append("?,");
            }



            String inClause = innerBuilder.deleteCharAt(
                    innerBuilder.length() - 1).toString();

            stmt = stmt + inClause;
            stmt = stmt + ")";


            if (++j < newLists.size()) {
                stmt = stmt + " OR ";
            }

        }

        stmt = stmt + sqlAfter;
        return stmt;
    }

    /**
     * Method to convert your SQL and a list of ID into a safe prepared
     * statements
     * 
     * @throws SQLException
     */
    public PreparedStatement prepareStatements(String sqlBefore,
            ArrayList<Integer> values, String sqlAfter, Connection c, String identifier)
            throws SQLException {

        /* First split our potentially big list into lots of lists */
        String stmt = genInClauseSql(sqlBefore, values, sqlAfter, identifier);
        PreparedStatement ps = c.prepareStatement(stmt);

        int i = 1;
        for (int val : values)
        {

            ps.setInt(i++, val);

        }
        return ps;

    }

}
于 2015-04-20T04:46:38.823 回答
2

Spring 允许将 java.util.Lists 传递给 NamedParameterJdbcTemplate,它会根据参数的数量自动生成 (?, ?, ?, ..., ?)。

对于 Oracle,这篇博文讨论了 oracle.sql.ARRAY 的使用(Connection.createArrayOf 不适用于 Oracle)。为此,您必须修改 SQL 语句:

SELECT my_column FROM my_table where search_column IN (select COLUMN_VALUE from table(?))

oracle table 函数将传递的数组转换为可在IN语句中使用的类似表的值。

于 2015-06-01T12:18:02.207 回答
1

尝试使用 instr 函数?

select my_column from my_table where  instr(?, ','||search_column||',') > 0

然后

ps.setString(1, ",A,B,C,"); 

诚然,这有点肮脏,但它确实减少了 sql 注入的机会。无论如何都可以在oracle中使用。

于 2008-10-07T14:13:09.070 回答
1

在 PreparedStatement 中生成查询字符串,以使多个 ? 与列表中的项目数相匹配。这是一个例子:

public void myQuery(List<String> items, int other) {
  ...
  String q4in = generateQsForIn(items.size());
  String sql = "select * from stuff where foo in ( " + q4in + " ) and bar = ?";
  PreparedStatement ps = connection.prepareStatement(sql);
  int i = 1;
  for (String item : items) {
    ps.setString(i++, item);
  }
  ps.setInt(i++, other);
  ResultSet rs = ps.executeQuery();
  ...
}

private String generateQsForIn(int numQs) {
    String items = "";
    for (int i = 0; i < numQs; i++) {
        if (i != 0) items += ", ";
        items += "?";
    }
    return items;
}
于 2009-12-16T12:42:48.790 回答
1

Sormula通过允许您提供 java.util.Collection 对象作为参数来支持 SQL IN 运算符。它创建一个带有 ? 对于集合中的每个元素。请参见示例 4(示例中的 SQL 是一个注释,用于阐明 Sormula 创建但未使用的内容)。

于 2011-11-21T19:30:54.997 回答
1

而不是使用

SELECT my_column FROM my_table where search_column IN (?)

使用 Sql 语句作为

select id, name from users where id in (?, ?, ?)

preparedStatement.setString( 1, 'A');
preparedStatement.setString( 2,'B');
preparedStatement.setString( 3, 'C');

或者使用存储过程,这将是最好的解决方案,因为 sql 语句将被编译并存储在数据库服务器中

于 2013-08-24T19:29:46.653 回答
1

我遇到了一些与准备好的声明相关的限制:

  1. 准备好的语句仅缓存在同一会话(Postgres)中,因此它仅适用于连接池
  2. @BalusC 提出的许多不同的预处理语句可能会导致缓存溢出,并且之前缓存的语句将被丢弃
  3. 必须优化查询并使用索引。听起来很明显,但是例如@Boris 在最佳答案之一中提出的 ANY(ARRAY...) 语句不能使用索引,尽管缓存,查询也会很慢
  4. 准备好的语句也会缓存查询计划,并且语句中指定的任何参数的实际值都不可用。

在提出的解决方案中,我会选择一个不会降低查询性能并减少查询次数的解决方案。这将是来自@Don 链接的#4(批处理少量查询)或为不需要的“?”指定 NULL 值。@Vladimir Dyuzhev 提出的标记

于 2013-09-12T07:21:19.157 回答
1

SetArray 是最好的解决方案,但它不适用于许多旧驱动程序。在 java8 中可以使用以下解决方法

String baseQuery ="SELECT my_column FROM my_table where search_column IN (%s)"

String markersString = inputArray.stream().map(e -> "?").collect(joining(","));
String sqlQuery = String.format(baseSQL, markersString);

//Now create Prepared Statement and use loop to Set entries
int index=1;

for (String input : inputArray) {
     preparedStatement.setString(index++, input);
}

此解决方案优于其他丑陋的 while 循环解决方案,其中查询字符串是通过手动迭代构建的

于 2018-02-28T17:55:44.360 回答
1

我刚刚为此制定了一个特定于 PostgreSQL 的选项。这有点像 hack,并且有其自身的优缺点和局限性,但它似乎可以工作,并且不限于特定的开发语言、平台或 PG 驱动程序。

诀窍当然是找到一种方法将任意长度的值集合作为单个参数传递,并让数据库将其识别为多个值。我工作的解决方案是从集合中的值构造一个分隔字符串,将该字符串作为单个参数传递,并使用 string_to_array() 以及 PostgreSQL 所需的强制转换以正确使用它。

因此,如果您想搜索“foo”、“blah”和“abc”,您可以将它们连接成一个字符串,如:'foo,blah,abc'。这是直接的SQL:

select column from table
where search_column = any (string_to_array('foo,blah,abc', ',')::text[]);

您显然会将显式转换更改为您希望结果值数组成为的任何内容——int、text、uuid 等。而且因为该函数采用单个字符串值(或者两个我想,如果您想自定义分隔符同样),您可以将其作为参数传递给准备好的语句:

select column from table
where search_column = any (string_to_array($1, ',')::text[]);

这甚至足够灵活,可以支持 LIKE 比较之类的事情:

select column from table
where search_column like any (string_to_array('foo%,blah%,abc%', ',')::text[]);

同样,毫无疑问,这是一个 hack,但它可以工作,并且允许您仍然使用预编译的准备好的语句,这些语句采用*ahem*离散参数,并具有伴随的安全性和(也许)性能优势。它是可取的并且实际上是有效的吗?当然,这取决于,因为您在查询运行之前就已经进行了字符串解析和可能的强制转换。如果您希望发送三个、五个、几十个值,当然,这可能没问题。几千?是的,也许没有那么多。YMMV,限制和排除适用,没有明示或暗示的保证。

但它有效。

于 2019-01-11T03:44:58.087 回答
1

似乎没有其他人建议使用现成的查询构建器,例如jOOQQueryDSL甚至是管理开箱即用的动态列表的Criteria Query,可能包括管理可能出现的所有边缘情况,例如:IN

  • 每个列表运行 Oracle 最多 1000 个元素IN(与绑定值的数量无关)
  • 遇到任何驱动程序的最大绑定值数量,我已在此答案中记录
  • 遇到游标缓存争用问题,因为太多不同的 SQL 字符串被“硬解析”并且无法再缓存执行计划(jOOQ 和最近 Hibernate 通过提供IN列表填充来解决这个问题)

(免责声明:我为 jOOQ 背后的公司工作)

于 2021-04-22T08:29:11.993 回答
0

只是为了完整性:只要值集不是太大,您可以简单地字符串构造一个语句,如

... WHERE tab.col = ? OR tab.col = ? OR tab.col = ?

然后您可以将其传递给 prepare(),然后在循环中使用 setXXX() 来设置所有值。这看起来很糟糕,但是许多“大型”商业系统通常会做这种事情,直到它们达到特定于 DB 的限制,例如 Oracle 中的语句为 32 KB(我认为是)。

当然,您需要确保集合永远不会过大,或者在发生错误时进行错误捕获。

于 2008-10-07T14:15:46.303 回答
0

按照亚当的想法。使您准备好的语句类似于 select my_column from my_table where search_column in (#) 创建一个字符串 x 并用一些“?,?,?”填充它 取决于您的值列表然后只需将查询中的 # 更改为您的新字符串 x 填充

于 2008-10-07T15:49:34.023 回答
0

对于 PreparedStatement 中的 IN 子句,我们可以使用不同的替代方法。

  1. 使用单个查询 - 最慢的性能和资源密集型
  2. 使用 StoredProcedure - 最快但特定于数据库
  3. 为 PreparedStatement 创建动态查询 - 性能良好,但没有从缓存中受益,并且每次都重新编译 PreparedStatement。
  4. 在 PreparedStatement 查询中使用 NULL - 最佳性能,当您知道 IN 子句参数的限制时效果很好。如果没有限制,则可以批量执行查询。示例代码片段是;

        int i = 1;
        for(; i <=ids.length; i++){
            ps.setInt(i, ids[i-1]);
        }
    
        //set null for remaining ones
        for(; i<=PARAM_SIZE;i++){
            ps.setNull(i, java.sql.Types.INTEGER);
        }
    

您可以在此处查看有关这些替代方法的更多详细信息。

于 2014-01-26T09:32:03.250 回答
0

在某些情况下,正则表达式可能会有所帮助。这是我在 Oracle 上检查过的一个示例,它可以工作。

select * from my_table where REGEXP_LIKE (search_column, 'value1|value2')

但是它有很多缺点:

  1. 它应用的任何列都应该转换为 varchar/char,至少是隐式的。
  2. 需要注意特殊字符。
  3. 它可能会降低性能——在我的例子中,IN 版本使用索引和范围扫描,而 REGEXP 版本进行完整扫描。
于 2015-01-02T06:23:42.467 回答
0

在检查了不同论坛中的各种解决方案并没有找到好的解决方案之后,我觉得我想出的以下 hack 是最容易遵循和编码的:

示例:假设您有多个参数要传入“IN”子句。只需在“IN”子句中放置一个虚拟字符串,例如,“PARAM”确实表示将代替该虚拟字符串的参数列表。

    select * from TABLE_A where ATTR IN (PARAM);

您可以将所有参数收集到 Java 代码中的单个字符串变量中。这可以按如下方式完成:

    String param1 = "X";
    String param2 = "Y";
    String param1 = param1.append(",").append(param2);

在我们的例子中,您可以将用逗号分隔的所有参数附加到单个字符串变量“param1”中。

将所有参数收集到一个字符串中后,您只需将查询中的虚拟文本(即本例中的“PARAM”)替换为参数字符串,即 param1。这是您需要做的:

    String query = query.replaceFirst("PARAM",param1); where we have the value of query as 

    query = "select * from TABLE_A where ATTR IN (PARAM)";

您现在可以使用 executeQuery() 方法执行查询。只需确保您的查询中的任何地方都没有“PARAM”一词。您可以使用特殊字符和字母的组合来代替单词“PARAM”,以确保查询中不会出现这样的单词。希望你得到解决方案。

注意:虽然这不是一个准备好的查询,但它完成了我希望我的代码完成的工作。

于 2015-03-15T14:56:44.103 回答
0

只是为了完整性,因为我没有看到其他人建议它:

在实施上述任何复杂建议之前,请考虑 SQL 注入是否确实是您的场景中的问题。

在许多情况下,提供给 IN (...) 的值是一个 id 列表,这些 id 的生成方式可以确保不可能进行注入...(例如,先前 select some_id from some_table where 的结果一些条件。)

如果是这种情况,您可能只是连接此值而不使用服务或准备好的语句,或者将它们用于此查询的其他参数。

query="select f1,f2 from t1 where f3=? and f2 in (" + sListOfIds + ");";
于 2015-04-01T18:12:37.637 回答
0

PreparedStatement 没有提供任何处理 SQL IN 子句的好方法。根据http://www.javaranch.com/journal/200510/Journal200510.jsp#a2 “您不能替换本来应该成为 SQL 语句一部分的东西。这是必要的,因为如果 SQL 本身可以更改,则驱动程序无法预编译语句。它还具有防止 SQL 注入攻击的良好副作用。” 我最终使用了以下方法:

String query = "SELECT my_column FROM my_table where search_column IN ($searchColumns)";
query = query.replace("$searchColumns", "'A', 'B', 'C'");
Statement stmt = connection.createStatement();
boolean hasResults = stmt.execute(query);
do {
    if (hasResults)
        return stmt.getResultSet();

    hasResults = stmt.getMoreResults();

} while (hasResults || stmt.getUpdateCount() != -1);
于 2017-06-07T21:05:11.477 回答
0

好的,所以我不记得我之前是如何(或在哪里)这样做的,所以我来堆栈溢出以快速找到答案。我很惊讶我做不到。

所以,很久以前我解决IN问题的方法是这样的:

其中 myColumn in ( select regexp_substr(:myList,'[^,]+', 1, level) from dual connect by regexp_substr(:myList, '[^,]+', 1, level) 不为空)

将 myList 参数设置为逗号分隔的字符串:A,B,C,D...

注意:您必须设置两次参数!

于 2021-08-19T19:16:51.500 回答
-1

这对我有用(伪代码):

public class SqlHelper
{
    public static final ArrayList<String>platformList = new ArrayList<>(Arrays.asList("iOS","Android","Windows","Mac"));

    public static final String testQuery = "select * from devices where platform_nm in (:PLATFORM_NAME)";
}

指定绑定:

public class Test extends NamedParameterJdbcDaoSupport
public List<SampleModelClass> runQuery()
{
    //define rowMapper to insert in object of SampleClass
    final Map<String,Object> map = new HashMap<>();
    map.put("PLATFORM_LIST",DeviceDataSyncQueryConstants.platformList);
    return getNamedParameterJdbcTemplate().query(SqlHelper.testQuery, map, rowMapper)
}
于 2020-01-15T07:19:13.463 回答
-3

我的解决方法(JavaScript)

    var s1 = " SELECT "

 + "FROM   table t "

 + "  where t.field in ";

  var s3 = '(';

  for(var i =0;i<searchTerms.length;i++)
  {
    if(i+1 == searchTerms.length)
    {
     s3  = s3+'?)';
    }
    else
    {
        s3  = s3+'?, ' ;
    }
   }
    var query = s1+s3;

    var pstmt = connection.prepareStatement(query);

     for(var i =0;i<searchTerms.length;i++)
    {
        pstmt.setString(i+1, searchTerms[i]);
    }

SearchTerms是包含您的输入/键/字段等的数组

于 2017-02-01T19:28:25.117 回答