在哪里(ID数组)

我有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); } 

你不能(不幸的是)那样做。 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语句嵌入整数(或日期时间,布尔值等)。

首先,你需要一个function和一个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,那么整个数据库函数都会失败。 我将error handling作为最终用户的练习。

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

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

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

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

所有功劳归功于Guru Brad Schulz的博客

访问接受多个Id值的T-SQL存储过程,以获取有关如何执行此操作的建议。

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

我的变量act =我的字符串ID。

act =“1,2,3,4”

  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); 

[的WebMethod]

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

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

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

你可以用它。 在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。 当您使用此function时,您可以在任何项目中使用该数据库。


感谢Microsoft MSDN。

Igo S Ventura

微软MVA

Sistema ArideSá

igo1-2@hotmail.com

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

这是我想到的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... } 
Interesting Posts