2008-10-08 37 views
24

Tôi có webservice được truyền một mảng int. Tôi muốn thực hiện lệnh chọn như sau nhưng vẫn gặp lỗi. Tôi có cần thay đổi mảng thành chuỗi không?WHERE IN (mảng ID)

[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); 
} 
+3

Nếu bạn đang sử dụng SQL Server 2008, bạn có thể sử dụng tham số có giá trị bảng cho phép bạn chuyển nhiều giá trị trong một tham số. Sau đó bạn sẽ có khả năng thực hiện một phép nối thay vì một "where in()", mặc dù một trong hai sẽ làm việc. [Tham số bảng giá trị] (http://msdn.microsoft.com/en-us/library/bb675163.aspx) – ulty4life

+0

Hmmm ... không nhận thấy rằng câu hỏi đã được hỏi vào tháng 10 năm 2008 lúc đầu. Vì vậy, có lẽ không ... Hy vọng rằng điều này ít nhất sẽ giúp đỡ bất cứ ai đánh này như là một kết quả tìm kiếm. – ulty4life

Trả lời

29

Bạn không thể (không may) làm điều đó. Tham số Sql chỉ có thể là một giá trị duy nhất, vì vậy bạn phải làm:

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

Tất nhiên, yêu cầu bạn phải biết có bao nhiêu id xây dựng hoặc xây dựng động truy vấn.

Là một workaround *, tôi đã thực hiện như sau:

WHERE buildingID IN (@buildingID) 

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

mà sẽ thay thế các văn bản của báo cáo kết quả với các con số, kết thúc như một cái gì đó như:

WHERE buildingID IN (1,2,3,4) 
  • Lưu ý rằng điều này là nhận được gần với một lỗ hổng Sql injection, nhưng vì nó là một mảng int là an toàn. Chuỗi tùy ý là không phải là an toàn, nhưng không có cách nào để nhúng câu lệnh Sql vào một số nguyên (hoặc datetime, boolean, v.v.).
+0

Một lưu ý với điều này là nếu 'buildingIDs' trống, mã đó sẽ tạo ra lỗi cú pháp và do đó bạn sẽ phải xử lý riêng trường hợp đó. –

6

LƯU Ý: Tôi thường không sử dụng các truy vấn không tham số. Tuy nhiên, trong CÀI ĐẶT NÀY, vì chúng tôi đang xử lý một mảng số nguyên, bạn có thể thực hiện một việc như vậy và sẽ hiệu quả hơn. Tuy nhiên, cho rằng mọi người dường như muốn hạ cấp câu trả lời vì nó không đáp ứng tiêu chí tư vấn hợp lệ của họ, tôi sẽ gửi một câu trả lời khác thực hiện khủng khiếp nhưng có lẽ sẽ chạy trong LINK2SQL.

Giả sử, như bang câu hỏi của bạn, rằng bạn có một mảng ints, bạn có thể sử dụng đoạn mã sau để trả về một chuỗi mà sẽ chứa một danh sách dấu phẩy phân cách mà SQL sẽ chấp nhận:

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

Sau đó, tôi muốn giới thiệu bạn bỏ cố gắng để parameterize lệnh cho rằng đây là một mảng ints và chỉ sử dụng:

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

Không, vui lòng không bỏ qua các câu lệnh được tham số hóa, đó là lời khuyên nguy hiểm để đưa ra! – Meff

+0

Tôi đồng ý. Vui lòng không đề xuất mọi người sử dụng truy vấn không được tham số. –

+0

Có ai có thể mở rộng những gì khiến điều này trở nên nguy hiểm không? – Jeffrey

0

[WebMethod]

công MiniEvent [] getAdminEvents (int buildingID, DateTime STARTDATE)

...

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

Có lẽ tôi đang được chi tiết, nhưng phương pháp này chấp nhận một int đơn lẻ, không phải là một mảng int. Nếu bạn mong đợi để vượt qua trong một mảng, bạn sẽ cần phải cập nhật định nghĩa phương thức của bạn để có một mảng int. Khi bạn nhận được mảng đó, bạn sẽ cần chuyển đổi mảng thành chuỗi nếu bạn định sử dụng nó trong truy vấn SQL.

8

Trước tiên, bạn sẽ cần một hàm và một sproc. Các chức năng sẽ chia dữ liệu của bạn và trả về một bảng:

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 

Tiếp theo, bạn cần một sproc sử dụng rằng:

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 

Cuối cùng, mã của bạn:

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

Đó đi cách xa hơn những gì câu hỏi của bạn hỏi nhưng nó sẽ làm những gì bạn cần.

Lưu ý: bạn nên chuyển vào bất kỳ thứ gì không phải là int, toàn bộ chức năng cơ sở dữ liệu sẽ không thành công. Tôi để lại lỗi xử lý cho điều đó như là một bài tập cho người dùng cuối.

3

Một Phương pháp XML siêu nhanh mà không cần mã không an toàn hoặc sử dụng xác định các chức năng:

Bạn có thể sử dụng một thủ tục lưu trữ và vượt qua các dấu phẩy tách ra danh sách các tòa 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)) 

Tất cả tín dụng đi vào Guru Brad Schulz's Blog

0

Bạn có thể sử dụng tính năng này. Thực hiện trong SQLServer để tạo ra một chức năng trên DB của bạn (Chỉ một lần):

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 
     [email protected] 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 

Sau khi tạo ra các chức năng bạn phải gọi đây trên mã của bạn:

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

Chức năng này có được dấu phẩy bên trong văn bản trên "mảng" và tạo một bảng có giá trị này là int, được gọi là ID. Khi chức năng này là trên bạn DB bạn có thể sử dụng trong bất kỳ dự án.


Nhờ Microsoft MSDN.

Igo S Ventura

Microsoft MVA

Sistema Ari de Sá

[email protected]

P.S .: Tôi đến từ Brazil. Xin lỗi về tiếng anh của tôi ... XD

1

Tôi sử dụng cách tiếp cận đó và làm việc cho tôi.

Hành động biến của tôi = danh sách ID của tôi theo chuỗi.

hành động = "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); 
0

Dưới đây là một giải pháp LINQ tôi nghĩ ra. Nó sẽ tự động chèn tất cả các mục trong danh sách dưới dạng tham số @ item0, @ item1, @ item2, @ item3, v.v.

[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... 
} 
Các vấn đề liên quan