INERE IN(ID数组)

时间:2008-10-08 10:33:13

标签: c# sql-server tsql ado.net sqlparameter

我有webservice,它传递了一组int。 我想按如下方式执行select语句,但不断出错。我是否需要将数组更改为字符串?

[WebMethod]
public MiniEvent[] getAdminEvents(int buildingID, DateTime startDate)
{    
    command.CommandText = @"SELECT id,
                            startDateTime, endDateTime From
                            tb_bookings WHERE buildingID IN
                            (@buildingIDs) AND startDateTime <=
                            @fromDate";

    SqlParameter buildID = new SqlParameter("@buildingIDs", buildingIDs);
}

9 个答案:

答案 0 :(得分:30)

你不能(不幸的是)那样做。 Sql参数只能是单个值,因此您必须执行:

WHERE buildingID IN (@buildingID1, @buildingID2, @buildingID3...)

当然,这要求您了解有多少建筑ID,或者动态构建查询。

作为解决方法*,我已完成以下工作:

WHERE buildingID IN (@buildingID)

command.CommandText = command.CommandText.Replace(
  "@buildingID", 
  string.Join(buildingIDs.Select(b => b.ToString()), ",")
);

将用数字替换语句的文本,最终结果如下:

WHERE buildingID IN (1,2,3,4)
  • 请注意,这是接近Sql注入漏洞,但因为它是一个int数组是安全的。任意字符串安全,但没有办法将Sql语句嵌入整数(或日期时间,布尔值等)。

答案 1 :(得分:8)

首先你需要一个函数和一个sproc。该函数将拆分数据并返回一个表:

CREATE function IntegerCommaSplit(@ListofIds nvarchar(1000))
returns @rtn table (IntegerValue int)
AS
begin
While (Charindex(',',@ListofIds)>0)
Begin
    Insert Into @Rtn 
    Select ltrim(rtrim(Substring(@ListofIds,1,Charindex(',',@ListofIds)-1)))
    Set @ListofIds = Substring(@ListofIds,Charindex(',',@ListofIds)+len(','),len(@ListofIds))
end
Insert Into @Rtn 
    Select  ltrim(rtrim(@ListofIds))
return 
end

接下来你需要一个sproc来使用它:

create procedure GetAdminEvents 
    @buildingids nvarchar(1000),
    @startdate datetime
as
SELECT id,startDateTime, endDateTime From
            tb_bookings t INNER JOIN 
dbo.IntegerCommaSplit(@buildingids) i
on i.IntegerValue = t.id
 WHERE startDateTime <= @fromDate

最后,您的代码:

[WebMethod]
        public MiniEvent[] getAdminEvents(int[] buildingIDs, DateTime startDate)
        command.CommandText = @"exec GetAdminEvents";
 SqlParameter buildID= new SqlParameter("@buildingIDs", buildingIDs);

这超出了你提出的问题,但它会做你需要的。

注意:如果你传入的不是int,那么整个数据库函数都会失败。我将错误处理作为最终用户的练习。

答案 2 :(得分:6)

注意:我通常不会使用非参数化查询。但是,在这个实例中,鉴于我们正在处理一个整数数组,你可以做这样的事情,它会更有效率。但是,鉴于每个人似乎都想降级答案,因为它不符合他们的有效建议标准,我将提交另一个表现可怕但可能在LINK2SQL中运行的答案。

假设您的问题表明您有一个int数组,您可以使用以下代码返回一个字符串,该字符串将包含SQL可以接受的逗号分隔列表:

private string SQLArrayToInString(Array a)
{
 StringBuilder sb = new StringBuilder();
 for (int i = 0; i < a.GetUpperBound(0); i++)
  sb.AppendFormat("{0},", a.GetValue(i));
 string retVal = sb.ToString();
 return retVal.Substring(0, retVal.Length - 1);
}

然后,我建议你跳过尝试参数化命令,假设这是一个int数组并且只使用:

command.CommandText = @"SELECT id,
            startDateTime, endDateTime From
            tb_bookings WHERE buildingID IN
            (" + SQLArrayToInString(buildingIDs) + ") AND startDateTime <=
            @fromDate";

答案 3 :(得分:3)

一种超高速XML方法,不需要不安全的代码或用户定义的函数:

您可以使用存储过程并传递以逗号分隔的建筑物ID列表:

Declare @XMLList xml
SET @XMLList=cast('<i>'+replace(@buildingIDs,',','</i><i>')+'</i>' as xml)
SELECT x.i.value('.','varchar(5)') from @XMLList.nodes('i') x(i))

所有功劳归于Guru Brad Schulz's Blog

答案 4 :(得分:1)

访问T-SQL stored procedure that accepts multiple Id values,了解有关如何执行此操作的建议。

答案 5 :(得分:1)

我使用这种方法并为我工作。

我的变量act =我在字符串中的ID列表。

  

act =&#34; 1,2,3,4&#34;

 command = new SqlCommand("SELECT x FROM y WHERE x.id IN (@actions)", conn);    
 command.Parameters.AddWithValue("@actions", act);
 command.CommandText = command.CommandText.Replace("@actions", act);

答案 6 :(得分:0)

  

[的WebMethod]

     

public MiniEvent [] getAdminEvents(int buildingID ,DateTime startDate)

     

...

     

SqlParameter buildID = new SqlParameter(“@ buildingIDs”, buildingIDs );

也许我过于详细,但这个方法接受一个int,而不是一个int数组。如果您希望传入一个数组,则需要更新方法定义以获得一个int数组。一旦获得该数组,如果计划在SQL查询中使用它,则需要将数组转换为字符串。

答案 7 :(得分:0)

你可以用它。在SQLServer中执行以在您的数据库上创建一个函数(仅一次):

IF EXISTS(
    SELECT *
    FROM sysobjects
    WHERE name = 'FN_RETORNA_ID_FROM_VARCHAR_TO_TABLE_INT')
BEGIN
    DROP FUNCTION FN_RETORNA_ID_FROM_VARCHAR_TO_TABLE_INT
END
GO

CREATE FUNCTION [dbo].FN_RETORNA_ID_FROM_VARCHAR_TO_TABLE_INT (@IDList VARCHAR(8000))
RETURNS
    @IDListTable TABLE (ID INT)
AS
BEGIN

    DECLARE
        --@IDList VARCHAR(100),
        @LastCommaPosition INT,
        @NextCommaPosition INT,
        @EndOfStringPosition INT,
        @StartOfStringPosition INT,
        @LengthOfString INT,
        @IDString VARCHAR(100),
        @IDValue INT

    --SET @IDList = '11,12,113'

    SET @LastCommaPosition = 0
    SET @NextCommaPosition = -1

    IF LTRIM(RTRIM(@IDList)) <> ''
    BEGIN

        WHILE(@NextCommaPosition <> 0)
        BEGIN

            SET @NextCommaPosition = CHARINDEX(',',@IDList,@LastCommaPosition + 1)

            IF @NextCommaPosition = 0
                SET @EndOfStringPosition = LEN(@IDList)
            ELSE
                SET @EndOfStringPosition = @NextCommaPosition - 1

            SET @StartOfStringPosition  = @LastCommaPosition + 1
            SET @LengthOfString = (@EndOfStringPosition + 1) - @StartOfStringPosition

            SET @IDString =  SUBSTRING(@IDList,@StartOfStringPosition,@LengthOfString)                  

            IF @IDString <> ''
                INSERT @IDListTable VALUES(@IDString)

            SET @LastCommaPosition = @NextCommaPosition

        END --WHILE(@NextCommaPosition <> 0)

    END --IF LTRIM(RTRIM(@IDList)) <> ''

    RETURN

ErrorBlock:

    RETURN

END --FUNCTION

创建函数后,您必须在代码中调用它:

command.CommandText = @"SELECT id,
                        startDateTime, endDateTime From
                        tb_bookings WHERE buildingID IN
                        (SELECT ID FROM FN_RETORNA_ID_FROM_VARCHAR_TO_TABLE_INT(@buildingIDs))) AND startDateTime <=
                        @fromDate";

command.Parameters.Add(new SqlParameter(){
                           DbType = DbType.String,
                           ParameterName = "@buildingIDs",
                           Value = "1,2,3,4,5" //Enter the parameters here separated with commas
                       });

此函数在“array”上获取文本内部逗号,并创建一个表,其值为int,称为ID。当您在DB上使用此功能时,您可以在任何项目中使用。

感谢Microsoft MSDN。

Igo S Ventura

Microsoft MVA

Sistema ArideSá

igo1-2@hotmail.com

P.S。:我来自巴西。向我的英语道歉... XD

答案 8 :(得分:0)

这是我想到的Linq解决方案。它会自动插入列表中的所有项目作为参数@ item0,@ item1,@ item2,@ item3等。

[WebMethod]
public MiniEvent[] getAdminEvents(Int32[] buildingIDs, DateTime startDate)
{
    // Gets a list with numbers from 0 to the max index in buildingIDs,
    // then transforms it into a list of strings using those numbers.
    String idParamString = String.Join(", ", (Enumerable.Range(0, buildingIDs.Length).Select(i => "@item" + i)).ToArray());
    command.CommandText = @"SELECT id,
                        startDateTime, endDateTime From
                        tb_bookings WHERE buildingID IN
                        (" + idParamString + @") AND startDateTime <=
                        @fromDate";
    // Reproduce the same parameters in idParamString 
    for (Int32 i = 0; i < buildingIDs.Length; i++)
            command.Parameters.Add(new SqlParameter ("@item" + i, buildingIDs[i]));
    command.Parameters.Add(new SqlParameter("@fromDate", startDate);
    // the rest of your code...
}