WHERE IN (массив идентификаторов)
У меня есть webservice, которому передается массив ints.
Я хотел бы сделать оператор 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);
}
Ответы
Ответ 1
Вы не можете (к сожалению) сделать это. Параметр 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, boolean и т.д.).
Ответ 2
Сначала вам понадобится функция и 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, вся функция базы данных завершится с ошибкой. Я оставляю обработку ошибок для этого как упражнение для конечного пользователя.
Ответ 3
ПРИМЕЧАНИЕ. Я обычно не использую непараметризированные запросы. В ЭТОЙ ИНСТАНЦИИ, однако, учитывая, что мы имеем дело с целым массивом, вы могли бы сделать такую вещь, и это было бы более эффективно. Однако, учитывая, что каждый, кажется, хочет понизить ответ, потому что он не соответствует их критериям правильных советов, я отправлю еще один ответ, который выполняется ужасно, но, вероятно, будет работать в LINK2SQL.
Предполагая, что в вашем вопросе утверждается, что у вас есть массив ints, вы можете использовать следующий код для возврата строки, которая будет содержать список с разделителями-запятыми, которые 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";
Ответ 4
Супербыстрый метод 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))
Весь кредит принадлежит Guru Блог Брэда Шульца
Ответ 5
Посетите хранимую процедуру T-SQL, которая принимает несколько значений Id за идеи о том, как это сделать.
Ответ 6
Я использую этот подход и работаю для меня.
Моя переменная 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);
Ответ 7
[WebMethod]
public MiniEvent [] getAdminEvents (int buildingID, DateTime startDate)
...
SqlParameter buildID = новый SqlParameter ( "@buildingIDs", buildingIDs);
Возможно, я слишком подробно, но этот метод принимает единственный int, а не массив ints. Если вы ожидаете пройти в массиве, вам нужно будет обновить определение метода, чтобы иметь массив int. Как только вы получите этот массив, вам нужно будет преобразовать массив в строку, если вы планируете использовать его в SQL-запросе.
Ответ 8
Вы можете использовать это. Выполнение в 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
[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
После создания функции вы должны вызвать это в своем коде:
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, называемую идентификатором. Когда эта функция находится на вашем БД, вы можете использовать в любом проекте.
Благодаря Microsoft MSDN.
Igo S Ventura
Microsoft MVA
Система Ари де Са
[email protected]
P.S.: Я из Бразилии. Извините мой английский... XD
Ответ 9
Вот решение 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...
}