JDBC中大型IN子句的替代方法,如何使用Java中的表值参数?

时间:2013-05-28 02:37:19

标签: java spring sql-server-2008 hibernate jdbc

我正在使用SQL Server 2008 RC2。

我们经常要做类似的查询:

select * from Site s where s.name in (:names)

":names"是1000个以逗号分隔的18字节名称的大StringBuilder。这需要一段时间才能运行;在我的本地开发机器上一分钟。我知道有很多人建议使用Table Valued Parameters(TVP),例如下面的示例:

create procedure [dbo].[sp_TestSites] @siteNames SiteNameTableType readonly
as
begin
    select s.* from Site s
       inner join @siteNames d on d.name = s.name
end

但是,由于Microsoft JDBC Driver仍然不支持TVP(据我所知),实现这一目标的最佳方法是什么?将一个逗号分隔值的长字符串发送到存储过程,然后在过程中拆分字符串并插入临时表?例如:

create type dbo.SiteNameTableType as table(
    name varchar(18) not null unique
)

create procedure sp_TestSites(
    @longStringOfNames varchar(max))
as
begin
    declare @siteNames SiteNameTableType

    insert into @siteNames
    select * from dbo.split(@longStringOfNames) -- assume I have a Split function

    select s.* from Site s
       inner join @siteNames d on d.name = s.name
end

2 个答案:

答案 0 :(得分:1)

正如我在评论中提到的,您可以将数据准备为XML字符串并将其传递给存储过程。在SP内部,您可以查询该XML并根据需要加入结果集。

答案 1 :(得分:1)

您还可以将CSV字符串传递给SP,使用SP中的FUNCTION将CSV字符串转换为TVP,然后使用它?

实施例:

CREATE FUNCTION [dbo].[GET_TABLE_FROM_CSVSTRING] ( @CSVString VARCHAR(MAX))
RETURNS  @Result TABLE(ItemValue VARCHAR(MAX)) 
AS
BEGIN

    DECLARE @x XML 
    SELECT @x = CAST('<A>'+ REPLACE(@CSVString,',','</A><A>')+ '</A>' AS XML)

    INSERT INTO @Result            
    SELECT t.value('.', 'VARCHAR(MAX)') AS inVal
    FROM @x.nodes('/A') AS x(t)

    RETURN
END   
GO

我不知道这是否是最快的字符串分割器,但我会稍微检查一下。 注意:为了在特定情况下获得更好的性能,请修改生成的TVP定义(和XML代码)以包含您期望的精确列域(以帮助查询优化器)。即如果您确定单个值的长度最多为50个字符,请不要使用VARCHAR(MAX)但要使用VARCHAR(50)。