2

可能重复:
在 MSSQL 中使用动态 IN 子句

我有一个包含参数的 SQL 语句,例如@StartRow, @StartEnd, @CategoryIds.

SELECT * FROM StackOverFlow
WHERE
StartRow = @StartRow
AND StartEnd = @StartEnd
AND CategoryId IN (@CategoryIds)

当我设置如下参数时:

DECLARE  @StartRow INT = 1;
DECLARE  @StartRow INT = 10;
DECLARE  @CategoryId NVARCHAR(50);
set @CategoryId ='124,125'

失败的消息是:

将 nvarchar 值“124,125”转换为数据类型 int 时转换失败。

当我手动设置 SQL where 语句时,如下所示:

WHERE
StartRow = @StartRow
AND StartEnd = @StartEnd
AND CategoryId IN (124, 125)

有用。

我该如何解决转换问题?

4

2 回答 2

3

为此,您需要使用动态 SQL。所以你会写

DECLARE  @StartRow INT = 1;
DECLARE  @StartRow INT = 10;
DECLARE @CatogoryIds NVARCHAR(50);
SET @CatogoryIds = N'124, 125';
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'
    SELECT * 
    FROM StackOverFlow
    WHERE
        StartRow = ' + @StartRow + 
      N' AND StartEnd = ' + @StartEnd + 
      N' AND CategoryId IN (' + @CategoryIds + N');';
EXEC(@SQL);
GO

我希望这有帮助。

于 2012-12-04T15:52:04.253 回答
2

如果使用动态 SQL,您应该像这样使用参数化查询和 SP_EXECUTESQL

CREATE TABLE T (StartRow INT, StartEnd INT, CategoryID INT);
INSERT T VALUES (1, 10, 124), (1, 10, 125), (1, 10, 126);

DECLARE  @StartRow INT = 1;
DECLARE  @StartEnd INT = 10;
DECLARE  @CategoryId NVARCHAR(50) ='124); DROP TABLE T; --';

DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT * 
            FROM    T 
            WHERE   StartRow = @Start
            AND     StartEnd = @End
            AND     CategoryID IN (' + @CategoryId + ')'

EXECUTE SP_EXECUTESQL @SQL, N'@Start INT, @End INT', @StartRow, @StartEnd;

动态 SQL 示例


您可以使用 SQL-Server 的 XML 扩展在没有动态 SQL 的情况下执行此操作,尽管我不确定您是否一定要这样做,但您可能需要运行一些测试来检查性能。

DECLARE  @StartRow INT = 1;
DECLARE  @StartEnd INT = 10;
DECLARE  @CategoryId NVARCHAR(50) ='124,125';


DECLARE @X XML = CAST(N'<root><catid>' + REPLACE(@CategoryID, ',', '</catid><catid>') + '</catid></root>' AS XML);

SELECT  StartRow,
        StartEnd,
        CategoryID
FROM    T
        INNER JOIN
        (   SELECT  [CatID] = cat.value('.', 'int')
            FROM    @X.nodes('/root/catid') c (cat)
        ) c
            ON c.CatID = CategoryID;

使用 XML 扩展的示例

该查询基本上将您的逗号分隔字符串转换为 XML,然后将 xml 拆分为它自己的表。

这也应该降低 sql 注入的风险,因为任何格式错误的字符串都会引发错误而不是运行。考虑这些变量:

DECLARE  @StartRow INT = 1;
DECLARE  @StartEnd INT = 10;
DECLARE  @CategoryId NVARCHAR(50) ='124,125); DROP TABLE T; --';

使用 XML 扩展方法运行会给出这个

Msg 245, Level 16, State 1, Line 13
Conversion failed when converting the nvarchar value '124,125); DROP TABLE T; --' to data type int.

XML 扩展的 SQL 注入失败

使用动态 SQL 运行会得到想要的结果,但是下次你运行它时,你会得到:

Msg 208, Level 16, State 1, Line 1
Invalid object name 'T'.

SQL 注入示例

除了上面使用的 XML 方法之外,还有其他拆分字符串的方法,但这只是为了证明动态 SQL 并不是唯一的前进方式,如果您无法控制输入字符串,那么不使用它会更明智因为恶意字符串会破坏您的数据!


为了答案的完整性,您还可以使用表值参数。例如

CREATE TYPE dbo.IntegerList AS TABLE (value INT);

CREATE PROCEDURE dbo.ExampleProcedure @StartRow INT, @StartEnd INT, @CategoryID dbo.IntegerList READONLY
AS
BEGIN

    SELECT  *
    FROM    T 
    WHERE   StartRow = @StartRow
    AND     StartEnd = @StartEnd
    AND     CategoryID IN (SELECT Value FROM @CategoryID)
END
于 2012-12-04T16:48:04.000 回答