13

在我的应用程序中,我想执行SELECT * FROM tbl WHERE col IN (@list)之类的查询,其中@list 可以有可变的值。我正在使用 MS SQL 服务器数据库。当我用谷歌搜索这个问题时,我发现了这个链接

http://www.sommarskog.se/arrays-in-sql-2008.html

该链接说使用表值参数。所以我使用 Microsoft SQL Server Management Studio 创建了用户定义的数据类型。

CREATE TYPE integer_list_tbltype AS TABLE (n int NOT NULL PRIMARY KEY)

然后我写了存储过程

CREATE PROCEDURE get_product_names @prodids integer_list_tbltype READONLY AS
   SELECT p.ProductID, p.ProductName
   FROM   Northwind.dbo.Products p
   WHERE  p.ProductID IN (SELECT n FROM @prodids)

然后只使用管理工作室我执行了这个过程

DECLARE @mylist integer_list_tbltype
INSERT @mylist(n) VALUES(9),(12),(27),(37)
EXEC get_product_names @mylist

它给了我正确的输出。但我想知道如何从 java 源代码调用这个存储过程。我知道如何用常数个参数调用简单的存储过程

CallableStatement proc_stmt = null;
proc_stmt = con.prepareCall("{call test(?)}");
proc_stmt.setString(1,someValue);

但是如何在表值参数情况下调用存储过程?

4

5 回答 5

8

这在JDBC 驱动程序手册中进行了记录。在你的情况下,你必须这样做:

try (SQLServerCallableStatement stmt =
    (SQLServerCallableStatement) con.prepareCall("{call test(?)}")) {

    SQLServerDataTable table = new SQLServerDataTable();   
    sourceDataTable.addColumnMetadata("n", java.sql.Types.INTEGER);   

    sourceDataTable.addRow(9);
    sourceDataTable.addRow(12);
    sourceDataTable.addRow(27);
    sourceDataTable.addRow(37);

    stmt.setStructured(1, "dbo.integer_list_tbltype", table);  
}

我最近也在一篇文章中记录了这一点

于 2017-02-01T10:45:03.440 回答
4

看起来这是对 JDBC 的计划添加,但尚未实现:

http://blogs.msdn.com/b/jdbcteam/archive/2012/04/03/how-would-you-use-table-valued-parameters-tvp.aspx

将参数作为分隔字符串 ("9,12,27,37") 传递,然后在 SQL Server 中创建一个名为 "fnSplit" 的表值函数或任何将在表中返回整数值的函数(只需搜索 "sql服务器拆分功能,”有数百万个)。

于 2013-04-16T22:09:28.863 回答
2

典型的答案(逗号分隔或 XML)都存在 SQL 注入问题。我需要一个允许我使用 PreparedStatement 的答案。所以我想出了这个:

StringBuilder query = new StringBuilder();
query.append(
  "DECLARE @mylist integer_list_tbltype;" +
  "INSERT @mylist(n) VALUES(?)");
for (int i = 0; i < values.size() - 1; ++i) {
  query.append(",(?) ");
}
query.append("; EXEC get_product_names @mylist ");
PreparedStatement preparedStmt = conn.prepareStatement(query.toString());
for (int i = 0; i < values.size(); ++i) {
    preparedStmt.setObject(i + 1, itemLookupValues.get(i));
}
于 2015-07-09T21:50:10.203 回答
2

现在它已被添加到 JDBC Driver 6.0。它CTP2呢。

“这个新的驱动程序现在支持表值参数和 Azure Active Directory。除了这些新功能之外,我们还为 Always Encrypted 添加了额外的功能。该驱动程序还支持国际化域名和参数化。”

https://blogs.msdn.microsoft.com/jdbcteam/2016/04/04/get-the-new-microsoft-jdbc-driver-6-0-preview/

下载链接:https ://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=11774

这是如何使用它的文档 https://msdn.microsoft.com/en-us/library/mt651781(v=sql.110).aspx

于 2016-06-12T00:35:04.343 回答
-7

在搜索了一段时间后,我找到了这个问题的答案。特别是当您使用 IN 子句并且没有操作数是可变的时,您可以使用逗号分隔的值作为 IN 子句中的输入。

下面是一个示例,该存储过程将在提供的邮政编码中检索给定律师类型的所有律师,该过程将如何使用动态 SQL。

CREATE PROCEDURE [dbo].[GetLawyers] ( @ZIP CHAR(5), @LawyerTypeIDs VARCHAR(100) )
AS

DECLARE @SQL     VARCHAR(2000)

SET @SQL = 'SELECT * FROM [dbo].[Lawyers]
            WHERE [ZIP] = ' + @ZIP + ' AND
                  [LawyerTypeID] IN (' + @LawyerTypeIDs + ')'
EXECUTE (@SQL)

GO

要执行传递用户输入的邮政编码和所选律师的存储过程,请输入逗号分隔值:

EXECUTE [dbo].[GetLawyers] '12345', '1,4'

所以结论是你不需要使用TVP。无论您使用哪种语言 [Java、PHP],只需将参数作为逗号分隔的字符串传递给存储过程,它就会完美运行。

所以在JAVA中你可以调用上面的存储过程:-

proc_stmt = con.prepareCall("{call GetLawyers(?,?)}");
proc_stmt.setString(1,"12345");
proc_stmt.setString(2,"'1,4'");
于 2013-04-18T14:21:52.427 回答