ГДЕ В (массив идентификаторов) - PullRequest
26 голосов
/ 08 октября 2008

У меня есть веб-сервис, которому передается массив целых чисел. Я хотел бы сделать оператор выбора следующим образом, но продолжаю получать ошибки. Нужно ли менять массив на строку?

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

30 голосов
/ 08 октября 2008

Вы не можете (к сожалению) сделать это. Параметр Sql может быть только одним значением, поэтому вам нужно будет сделать:

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

Что, конечно, требует, чтобы вы знали, сколько существует идентификаторов зданий, или для динамического построения запроса.

В качестве обходного пути * я сделал следующее:

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 в целое число (или datetime, логическое значение и т. Д.).
8 голосов
/ 15 октября 2008

Сначала вам понадобится функция и 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

Далее вам понадобится спрок, чтобы использовать это:

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, вся функция базы данных потерпит неудачу Я оставляю обработку ошибок для этого как упражнение для конечного пользователя.

6 голосов
/ 08 октября 2008

ПРИМЕЧАНИЕ: Обычно я не использую непараметризованные запросы. В этом случае, однако, учитывая, что мы имеем дело с целочисленным массивом, вы могли бы сделать такую ​​вещь, и это было бы более эффективно. Однако, учитывая, что все, похоже, хотят понизить ответ, потому что он не соответствует их критериям действительного совета, я отправлю другой ответ, который работает ужасно, но, вероятно, будет работать в LINK2SQL.

Если в вашем вопросе указано, что у вас есть массив целых чисел, вы можете использовать следующий код для возврата строки, которая будет содержать список с разделителями-запятыми, который будет принимать 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);
}

Тогда я бы порекомендовал вам пропустить попытку параметризации команды , учитывая, что это массив целых чисел , и просто использовать:

command.CommandText = @"SELECT id,
            startDateTime, endDateTime From
            tb_bookings WHERE buildingID IN
            (" + SQLArrayToInString(buildingIDs) + ") AND startDateTime <=
            @fromDate";
3 голосов
/ 04 марта 2013

Сверхбыстрый метод XML, который не требует небезопасного кода или пользовательских функций:

Вы можете использовать хранимую процедуру и передать список идентификаторов зданий через запятую:

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

Вся заслуга принадлежит Гуру Блог Брэда Шульца

1 голос
/ 26 августа 2016

Я использую этот подход и работает для меня.

Моя переменная act = мой список идентификаторов в строке.

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 голос
/ 08 октября 2008
0 голосов
/ 25 ноября 2016

Вот решение 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 голосов
/ 16 июля 2015

Вы можете использовать это. Выполните в 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
                       });

Эта функция получает текстовые запятые в массиве и создает таблицу с такими значениями как int, которая называется ID Когда эта функция находится на вашей БД, вы можете использовать ее в любом проекте.


Благодаря Microsoft MSDN.

Igo S Ventura

Microsoft MVA

Система Ари де Са

igo1-2@hotmail.com

П.С .: Я из Бразилии. Извиняюсь за мой английский ... XD

0 голосов
/ 15 октября 2008

[WebMethod]

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

...

SqlParameter buildID = новый SqlParameter ("@ buildingIDs", buildingIDs );

Возможно, я слишком подробен, но этот метод принимает одно целое, а не массив целых чисел. Если вы ожидаете передать массив, вам нужно обновить определение метода, чтобы получить массив int. Как только вы получите этот массив, вам нужно будет преобразовать массив в строку, если вы планируете использовать его в запросе SQL.

...