JDBC SQl 语句 IN 参数的格式是啥

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC SQl 语句 IN 参数的格式是啥相关的知识,希望对你有一定的参考价值。

我jdbc做数据库操作,有参数放在in关键字的地方
String sql = " update Table set name = 'TestName' where type in ? ";
我想查询type 是 typeone 和 typetwo typethree 的数据。所以我把参数写成 如下:
ps.setString(1, "typeone','typeone','typethree");
但是数据库没有修改,而且也没报错,我都试了好几个格式了 都不好使
请问这个in 参数的正确格式应该是怎么样。谢谢。
String sql = " update Table set name = 'TestName' where type in (?) ";
加了()也没好使,
另外 参数个数是不固定的 所以只能拼sql到一个参数里传过去。

参考技术A IN 操作符允许我们在 WHERE 子句中规定多个值。
SQL IN 语法
SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1,value2,...)

例子:
Persons 表:

Id LastName FirstName Address City

1 Adams John Oxford Street London
2 Bush George Fifth Avenue New York
3 Carter Thomas Changan Street Beijing
IN 操作符实例
现在,我们希望从上表中选取姓氏为 Adams 和 Carter 的人:
我们可以使用下面的 SELECT 语句:
SELECT * FROM Persons
WHERE LastName IN ('Adams','Carter')

结果集:

Id
LastName FirstName Address City

1 Adams John Oxford Street London
3 Carter Thomas Changan Street Beijing
参考技术B

需要使用for循环拼接参数,不能直接这样操作

SELECT * FROM TABLE_A WHERE A_NAME IN(?)
ps.setString(1,"想要拼的字符串");这种写法严重错误

参考技术C update Table set name = 'TestName' where type in (?,?,?);

追问

。。。我可能没说明白,问题是in里面的参数不固定。所以要拼成一个参数然后放进去。所以要把这三个拼成一个string放进去

参考技术D 你应该把参数放到()中把追问

String sql = " update Table set name = 'TestName' where type in (?) ";

ps.setString(1, "typeone','typeone','typethree");

加了() 还是没效果,不知道string参数拼的对不对 。

追答

那就分成三个分别进行传入值,为什么非要放在一起呢,你的个数不确定,你就用循环进行操作,你有几个,你就写几个参数,传值的时候,你再次进行循环不就行了

追问

但那样太多次调用数据库了,性能不好吧(虽然in参数多了性能也不行,但也比循环execute好吧)

追答

没叫你在数据库里,我让你在java进行循环,写ps.setString(1, "typeone'");这个东西

本回答被提问者采纳

使用 JDBC 参数化 IN 子句的最佳方法是啥? [复制]

【中文标题】使用 JDBC 参数化 IN 子句的最佳方法是啥? [复制]【英文标题】:What is the best approach using JDBC for parameterizing an IN clause? [duplicate]使用 JDBC 参数化 IN 子句的最佳方法是什么? [复制] 【发布时间】:2011-02-21 02:51:21 【问题描述】:

说我有一个表格查询

SELECT * FROM MYTABLE WHERE MYCOL in (?)

我想将参数参数化为 in。

是否有一种直接的方法可以在 Java 中使用 JDBC 执行此操作,并且可以在不修改 SQL 本身的情况下在多个数据库上工作?

最接近的question I've found had to do with C#,我想知道Java/JDBC 是否有不同之处。

【问题讨论】:

【参考方案1】:

我从docs.spring(19.7.3)得到了答案

SQL 标准允许基于包含变量值列表的表达式来选择行。一个典型的例子是 select * from T_ACTOR where id in (1, 2, 3)。 JDBC 标准不直接支持准备好的语句使用此变量列表;您不能声明可变数量的占位符。您需要准备好所需数量的占位符的多种变体,或者您需要在知道需要多少占位符后动态生成 SQL 字符串。 NamedParameterJdbcTemplate 和 JdbcTemplate 中提供的命名参数支持采用后一种方法。将值作为原始对象的 java.util.List 传递。此列表将用于在语句执行期间插入所需的占位符并传入值。

希望对你有帮助。

【讨论】:

【参考方案2】:

在 JDBC 中确实没有直接的方法可以做到这一点。 一些 JDBC 驱动程序似乎支持IN 子句中的PreparedStatement#setArray()。我只是不确定哪些是。

您可以只使用带有String#join()Collections#nCopies() 的辅助方法来生成IN 子句的占位符,并使用另一个辅助方法来使用PreparedStatement#setObject() 在循环中设置所有值。

public static String preparePlaceHolders(int length) 
    return String.join(",", Collections.nCopies(length, "?"));


public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException 
    for (int i = 0; i < values.length; i++) 
        preparedStatement.setObject(i + 1, values[i]);
    

你可以这样使用它:

private static final String SQL_FIND = "SELECT id, name, value FROM entity WHERE id IN (%s)";

public List<Entity> find(Set<Long> ids) throws SQLException 
    List<Entity> entities = new ArrayList<Entity>();
    String sql = String.format(SQL_FIND, preparePlaceHolders(ids.size()));

    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(sql);
    ) 
        setValues(statement, ids.toArray());

        try (ResultSet resultSet = statement.executeQuery()) 
            while (resultSet.next()) 
                entities.add(map(resultSet));
            
        
    

    return entities;


private static Entity map(ResultSet resultSet) throws SQLException 
    Enitity entity = new Entity();
    entity.setId(resultSet.getLong("id"));
    entity.setName(resultSet.getString("name"));
    entity.setValue(resultSet.getInt("value"));
    return entity;

请注意,某些数据库在IN 子句中对允许的值数量有限制。例如,Oracle 对 1000 个项目有此限制。

【讨论】:

这种方式会不会导致SQL注入?? @Kaylan:没有单个代码行将用户控制的原始输入添加到 SQL 查询字符串中。所以绝对没有 SQL 注入风险。 很好。感谢您的澄清 jtds 驱动程序的最大参数变量列表为 2,000。 在 MySQL(驱动程序 5.1.37)和 PostgreSQL(驱动程序 9.1-901)之间,只有 PostgreSQL 对 PreparedStatement#setArray() 有一些支持【参考方案3】:

sormula 让这一切变得简单(参见Example 4):

ArrayList<Integer> partNumbers = new ArrayList<Integer>();
partNumbers.add(999);
partNumbers.add(777);
partNumbers.add(1234);

// set up
Database database = new Database(getConnection());
Table<Inventory> inventoryTable = database.getTable(Inventory.class);

// select operation for list "...WHERE PARTNUMBER IN (?, ?, ?)..."
for (Inventory inventory: inventoryTable.
    selectAllWhere("partNumberIn", partNumbers))    

    System.out.println(inventory.getPartNumber());

【讨论】:

【参考方案4】:

我们可以使用不同的替代方法。

    执行单个查询 - 速度慢且不推荐 使用存储过程 - 特定于数据库 动态创建 PreparedStatement 查询 - 性能良好,但缓存的好处松散,需要重新编译 在 PreparedStatement 查询中使用 NULL - 我认为这是一种具有最佳性能的好方法。

查看有关这些here 的更多详细信息。

【讨论】:

【参考方案5】:

执行此操作的标准方法是(如果您使用 Spring JDBC)是使用 org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate 类。

使用这个类,可以定义一个 List 作为你的 SQL 参数,并使用 NamedParameterJdbcTemplate 来替换一个命名参数。例如:

public List<MyObject> getDatabaseObjects(List<String> params) 
    NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
    String sql = "select * from my_table where my_col in (:params)";
    List<MyObject> result = jdbcTemplate.query(sql, Collections.singletonMap("params", params), myRowMapper);
    return result;

【讨论】:

【参考方案6】:

由于没有人回答 大型 IN 子句(超过 100 个),我将提出我的解决方案来解决这个问题,该解决方案非常适用于 JDBC。简而言之,我将 tmp 表上的 IN 替换为 INNER JOIN

我所做的是创建一个我称之为批处理 ids 的表,根据 RDBMS,我可以创建一个 tmp 表或内存表。

表格有两列。一列带有来自 IN 子句的 ID,另一列带有我即时生成的批次 ID。

SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?

在您选择之前,将您的 id 放入具有给定批次 id 的表中。 然后,您只需将原始查询 IN 子句替换为与您的 ids 表匹配的 INNER JOIN WHERE batch_id 等于您当前的批次。完成后,您可以批量删除条目。

【讨论】:

+1 这对于大型数据集非常有效,并且不会破坏您的数据库 嗯,半连接(INEXISTS 谓词)不会胜过内连接吗? @LukasEder YMMV 和我的 SQL 伪代码。一如既往的测试/基准测试。这是一个有趣的想法。说到这里,当我们这样做时,我应该去看看我们的实际 SQL 是做什么的。 确实如此。在 MySQL 上,半连接仍然有变慢的风险。但是内部连接有不正确的风险;) @Male,当您需要从包含所有结果的 JDBC 函数(例如,使用商定/固定的 API)输出完整的 ResultSet 时,这将是一个不错的选择:填写 temp-表并进行连接,而不是批量输入,而无法合并 ResultSet。 (注意:当您只有 SELECT 权限时,您将无法创建临时表)【参考方案7】:

看到我的试用成功了,据说列表大小有潜在的限制。 列表 l = Arrays.asList(new Integer[]12496,12497,12498,12499); 地图参数 = Collections.singletonMap("goodsid",l);

    NamedParameterJdbcTemplate  namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());
    String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid in(:goodsid)";
    List<Long> list = namedParameterJdbcTemplate.queryForList(sql, param2, Long.class);

【讨论】:

【参考方案8】:

AFAIK,JDBC 中没有标准支持将集合作为参数处理。如果您可以只传入一个 List 并将其扩展,那就太好了。

Spring 的 JDBC 访问支持将集合作为参数传递。您可以看看这是如何完成的,以获得安全编码的灵感。

见Auto-expanding collections as JDBC parameters

(本文先讨论 Hibernate,然后再讨论 JDBC。)

【讨论】:

【参考方案9】:

我能想到的一种方法是使用 java.sql.PreparedStatement 和一些陪审团操纵

PreparedStatement PreparedStmt = conn.prepareStatement("SELECT * FROM MYTABLE WHERE MYCOL in (?)");

...然后...

preparedStmt.setString(1, [你的字符串参数]);

http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html

【讨论】:

这将不起作用,因为它可能会创建像 ... WHERE MYCOL IN ('2,3,5,6') 这样的查询,而这不是您想要做的。【参考方案10】:

我通过使用尽可能多的 ? 构造 SQL 字符串来解决这个问题。

SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)

首先,我搜索了可以传递到语句中的数组类型,但所有 JDBC 数组类型都是特定于供应商的。所以我留在了多个?

【讨论】:

这就是我们现在正在做的事情,但我希望有一种统一的方法可以在没有自定义 SQL 的情况下做到这一点...... 另外,如果它类似于 Oracle,它必须重新解析大多数语句。

以上是关于JDBC SQl 语句 IN 参数的格式是啥的主要内容,如果未能解决你的问题,请参考以下文章

Spring jdbc模板:将sql语句保留在代码之外的最佳方法是啥

java jdbc sql语句参数的设置

怎么解决这个问题?是啥问题 sql语句在PL/SQL里运行没问题啊

在 sql 查询中使用 not in 时优化此 SQL 语句的最佳方法是啥?

在JDBC中使用带参数的SQL语句

在SQL语句中,in和exist的区别是啥?