web-dev-qa-db-ja.com

WHERE IN(IDの配列)

Intの配列が渡されるwebserviceがあります。次のように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);
}
27
user17510

(残念ながら)それはできません。 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配列なので安全です。任意の文字列はnot安全ですが、整数(またはdatetime、booleanなど)にSqlステートメントを埋め込む方法はありません。
31
Mark Brackett

まず、関数と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以外のものを渡すと、データベース関数全体が失敗します。そのためのエラー処理は、エンドユーザーの演習として残しておきます。

8
Josef

注:私は一般的に、パラメータ化されていないクエリを使用するためではありません。ただし、このインスタンスでは、整数配列を処理していることを考えると、couldを実行すると、より効率的になります。ただし、有効なアドバイスの基準を満たしていないため、全員が回答をダウングレードしたいと考えているため、恐ろしく実行されるがおそらく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";
6
Josef

安全でないコードやユーザー定義関数を必要としない超高速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))

すべてのクレジットは達人になります Brad Schulzのブログ

3
Nishant

複数のId値を受け入れるT-SQLストアドプロシージャ にアクセスして、これを行う方法のアイデアをご覧ください。

1
jop

私はそのアプローチを使用し、私のために働きます。

私の変数act = stringの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);
1
Gonçalo Dinis

[WebMethod]

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

...

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

おそらく私は詳しく説明しすぎていますが、このメソッドはintの配列ではなく単一のintを受け入れます。配列を渡す場合、メソッド定義を更新してint配列を持つ必要があります。その配列を取得したら、SQLクエリで使用する予定がある場合は、配列を文字列に変換する必要があります。

0
Chris Porter

これが私が考えた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...
}
0
Nyerguds

これを使用できます。 SQLServerで実行して、DBに関数を作成します(1回のみ):

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

この関数は、「配列」のテキスト内部コンマを取得し、IDと呼ばれるこの値をintとしてテーブルを作成します。この機能がDB上にある場合、任意のプロジェクトで使用できます。


Microsoft MSDNに感謝します。

イゴSベンチュラ

Microsoft MVA

システマ・アリ・デ・サー

[email protected]

追伸:私はブラジル出身です。私の英語を謝罪... XD

0
Igo Soares