2

这是我这些天正在破解的坚果

我正在处理的应用程序对 SQL 进行了一些高级处理。其中一项操作根据集合中的项目名称从不同的表中选择有关当前上下文中对象的各种元数据。为此,执行了一系列“select...from...where...in()”,为了防止恶意SQL代码,使用Sql参数构造“in()”子句的内容。

但是,当用于构造“in()”子句的项目集合大于 2100 个项目时,由于 Sql Server 限制每个查询最多 2100 个 Sql 参数,这将失败。

我现在尝试的一种方法是创建一个#temp 表来存储所有项目名称,然后在原始查询中加入该表,而不是使用“where in()”。这让我对如何使用存储在 .NET 代码中的数组中的项目名称来填充表感到头疼。当然,必须有一些批量方法来插入所有内容,而不是为每个项目发出单独的“插入”?

除此之外,我对解决此问题的替代方法非常感兴趣。

非常感谢

4

7 回答 7

5

一种可能的解决方法是使用查询 XML 的功能,只需将“in”的所有数据作为 xml 列发送,然后加入该列。

可以使用相同的方法来填充您的临时表,但话又说回来,为什么不直接使用它。

这是一个应该说明的简短示例:

declare @wanted xml
set @wanted = '<ids><id>1</id><id>2</id></ids>'
select * 
from (select 1 Id union all select 3) SourceTable 
where Id in(select Id.value('.', 'int') from @wanted.nodes('/ids/id') as Foo(Id))

只需在您的应用程序中构建 xml 并将其作为参数传递。

于 2008-10-03T11:03:54.767 回答
1

Hrm,在不了解上下文以及有关数据以及您如何使用结果和性能问题的更多信息的情况下,我将尝试提出替代方案。你可以分成多个查询吗?像现在一样做同样的事情,但不是构建一个包含 2100+ 个项目的查询,而是构建两个每个包含 1050 个的查询,然后合并结果。

于 2008-10-03T11:00:45.540 回答
1

预防恶意 SQL 代码: > 使用存储过程。

是的,SQL Server 2005 有一个批量插入:http: //msdn.microsoft.com/en-us/library/ms188365.aspx

于 2008-10-03T11:04:37.683 回答
1

您可以使用 .NET 2.0 引入的 SqlBulkCopy 类。它实际上非常易于使用。看看这个:

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx

于 2008-10-03T11:20:37.090 回答
0

对于批量更新问题:查看其中包含数据表的数据适配器。您可以设置一个参数,允许您批量插入/更新表中的项目,并且您可以选择批量中的项目数 MSDN文章

您似乎应该仔细研究业务问题或领域,以确定更好的方法来过滤查询中的项目。IN() 子句可能不是您执行此操作的最佳方式。在您的情况下,添加数据类别或过滤器而不是要包含的大量项目可能会更好。在不了解业务问题/背景的情况下,很难说。

于 2008-10-03T11:11:46.860 回答
0

好的,我不确定这对您有多好或性能如何,但这是我过去用来实现类似功能的一些代码:

    CREATE FUNCTION [dbo].[Split](
    @list ntext
)
RETURNS @tbl TABLE (listpos int IDENTITY(1, 1) NOT NULL,
                          number  int NOT NULL) 
AS
BEGIN
    DECLARE @pos      int,
            @textpos  int,
            @chunklen smallint,
            @str      nvarchar(4000),
            @tmpstr   nvarchar(4000),
            @leftover nvarchar(4000)

    SET @textpos = 1
    SET @leftover = ''
    WHILE @textpos <= datalength(@list) / 2
    BEGIN
       SET @chunklen = 4000 - datalength(@leftover) / 2
       SET @tmpstr = ltrim(@leftover + substring(@list, @textpos, @chunklen))
       SET @textpos = @textpos + @chunklen

       SET @pos = charindex(',', @tmpstr)
       WHILE @pos > 0
       BEGIN
          SET @str = substring(@tmpstr, 1, @pos - 1)
          INSERT @tbl (number) VALUES(convert(int, @str))
          SET @tmpstr = ltrim(substring(@tmpstr, @pos + 1, len(@tmpstr)))
          SET @pos = charindex(',', @tmpstr)
       END

       SET @leftover = @tmpstr
    END

    IF ltrim(rtrim(@leftover)) <> ''
       INSERT @tbl (number) VALUES(convert(int, @leftover))

    RETURN
END

然后在您的其他存储过程中,您可以传入以逗号分隔的 ID 字符串,例如:

select a.number from split('1,2,3') a inner join myothertable b on a.number = b.ID

就像我说的那样,这可能真的很糟糕,因为它包含很多字符串操作,我不记得我从哪里得到这个函数......但它在那里可供挑选......

我想如果你真的不需要索引原始字符串,你也可以去掉填充 listpos 列的位。

于 2008-10-03T11:23:06.617 回答
0

SQL Server 2008 将具有表参数。这就是你想要的锤子。

于 2008-10-03T13:48:25.363 回答