INERE IN(ID数组)

use*_*510 27 c# t-sql sql-server 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);
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ett 32

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

WHERE buildingID IN (@buildingID1, @buildingID2, @buildingID3...)
Run Code Online (Sandbox Code Playgroud)

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

作为一种解决方法*,我做了以下事情:

WHERE buildingID IN (@buildingID)

command.CommandText = command.CommandText.Replace(
  "@buildingID", 
  string.Join(buildingIDs.Select(b => b.ToString()), ",")
);
Run Code Online (Sandbox Code Playgroud)

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

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

  • 需要注意的是,如果“buildingIDs”为空,该代码将产生语法错误,因此您必须单独处理这种情况。 (2认同)

Jos*_*sef 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
Run Code Online (Sandbox Code Playgroud)

接下来你需要一个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
Run Code Online (Sandbox Code Playgroud)

最后,你的代码:

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

这远远超出了你的问题,但它会做你需要的.

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


Jos*_*sef 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 不,请不要跳过参数化声明,这是危险的建议! (2认同)

jop*_*jop 1

请访问接受多个 Id 值的 T-SQL 存储过程,了解如何执行此操作。