Как передать массив в хранимую процедуру SQL Server?
Например, у меня есть список сотрудников. Я хочу использовать этот список в виде таблицы и присоединить его к другой таблице. Но список сотрудников должен быть передан как параметр из С#.
Как передать массив в хранимую процедуру SQL Server?
Например, у меня есть список сотрудников. Я хочу использовать этот список в виде таблицы и присоединить его к другой таблице. Но список сотрудников должен быть передан как параметр из С#.
Поскольку у вас уже есть список, я думаю, что есть более простые способы, чем XML.
Сначала в вашей базе данных создайте следующие два объекта:
CREATE TYPE dbo.EmployeeList
AS TABLE
(
EmployeeID INT
);
GO
CREATE PROCEDURE dbo.DoSomethingWithEmployees
@List AS dbo.EmployeeList READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT EmployeeID FROM @List;
END
GO
Теперь в коде С#:
DataTable tvp = new DataTable();
// define / populate DataTable from your List here
using (conn)
{
SqlCommand cmd = new SqlCommand("dbo.DoSomethingWithEmployees", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvparam = cmd.Parameters.AddWithValue("@List", tvp);
tvparam.SqlDbType = SqlDbType.Structured;
// execute query, consume results, etc. here
}
Если вы используете SQL Server 2005, я бы по-прежнему рекомендовал функцию разделения по XML. Сначала создайте функцию:
CREATE FUNCTION dbo.SplitInts
(
@List VARCHAR(MAX),
@Delimiter VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT Item = CONVERT(INT, Item) FROM
( SELECT Item = x.i.value('(./text())[1]', 'varchar(max)')
FROM ( SELECT [XML] = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>') + '</i>').query('.')
) AS a CROSS APPLY [XML].nodes('i') AS x(i) ) AS y
WHERE Item IS NOT NULL
);
GO
Теперь ваша хранимая процедура может быть только:
CREATE PROCEDURE dbo.DoSomethingWithEmployees
@List VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
SELECT EmployeeID = Item FROM dbo.SplitInts(@List, ',');
END
GO
И в коде С# вам просто нужно передать список как '1,2,3,12'
...
Я рекомендую вам сравнить ремонтопригодность и производительность этих опций с выбранным методом.
Основываясь на моем опыте, создавая разграниченное выражение из идентификаторов employeeID, для этой проблемы есть сложное и приятное решение. Вы должны создать только строковое выражение типа ';123;434;365;'
, в котором 123
, 434
и 365
- некоторые идентификаторы employeeID. Вызвав приведенную ниже процедуру и передав ей это выражение, вы можете получить нужные записи. Вы легко можете присоединиться к "другой таблице" в этот запрос. Это решение подходит для всех версий SQL-сервера. Кроме того, по сравнению с использованием табличной переменной или таблицы temp это очень быстрое и оптимизированное решение.
CREATE PROCEDURE dbo.DoSomethingOnSomeEmployees @List AS varchar(max)
AS
BEGIN
SELECT EmployeeID
FROM EmployeesTable
-- inner join AnotherTable on ...
where @List like '%;'+cast(employeeID as varchar(20))+';%'
END
GO
Используйте хранимый в таблице параметр для хранимой процедуры.
Когда вы передадите его с С#, вы добавите параметр с типом данных SqlDb.Structured.
Смотрите здесь: http://msdn.microsoft.com/en-us/library/bb675163.aspx
Пример:
// Assumes connection is an open SqlConnection object.
using (connection)
{
// Create a DataTable with the modified rows.
DataTable addedCategories =
CategoriesDataTable.GetChanges(DataRowState.Added);
// Configure the SqlCommand and SqlParameter.
SqlCommand insertCommand = new SqlCommand(
"usp_InsertCategories", connection);
insertCommand.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = insertCommand.Parameters.AddWithValue(
"@tvpNewCategories", addedCategories);
tvpParam.SqlDbType = SqlDbType.Structured;
// Execute the command.
insertCommand.ExecuteNonQuery();
}
Вам нужно передать его как параметр XML.
Изменить: быстрый код из моего проекта, чтобы дать вам представление:
CREATE PROCEDURE [dbo].[GetArrivalsReport]
@DateTimeFrom AS DATETIME,
@DateTimeTo AS DATETIME,
@HostIds AS XML(xsdArrayOfULong)
AS
BEGIN
DECLARE @hosts TABLE (HostId BIGINT)
INSERT INTO @hosts
SELECT arrayOfUlong.HostId.value('.','bigint') data
FROM @HostIds.nodes('/arrayOfUlong/u') as arrayOfUlong(HostId)
Затем вы можете использовать таблицу temp для соединения с вашими таблицами. Мы определили arrayOfUlong как построенную в XML-схему для обеспечения целостности данных, но вам не нужно это делать. Я бы рекомендовал использовать его, чтобы здесь был быстрый код, чтобы убедиться, что вы всегда получаете XML с longs.
IF NOT EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'xsdArrayOfULong')
BEGIN
CREATE XML SCHEMA COLLECTION [dbo].[xsdArrayOfULong]
AS N'<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="arrayOfUlong">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded"
name="u"
type="xs:unsignedLong" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>';
END
GO
Контекст всегда важен, например размер и сложность массива. Для небольших и средних списков несколько ответов, размещенных здесь, просто прекрасны, хотя некоторые разъяснения должны быть сделаны:
text()
XML. Для получения дополнительной информации (например, анализа производительности) при использовании XML для разбиения списков проверьте "Использование XML для передачи списков в качестве параметров в SQL Server" от Phil Factor.DataTable
означает дублирование данных в памяти при копировании из исходной коллекции. Следовательно, использование метода DataTable
для передачи в TVP не работает хорошо для больших наборов данных (т.е. Плохо масштабируется).DataTable
TVP, XML не масштабируется, так как он более чем удваивает объем данных в памяти, поскольку ему необходимо дополнительно учитывать накладные расходы XML-документа.При всем том, что, если данные, которые вы используете, являются большими или не очень большими, но постоянно растут, то метод IEnumerable
TVP - лучший выбор, поскольку он передает данные на SQL Server (например, t21 > ), но не требует дублирования коллекции в памяти (в отличие от любого другого метода). В этом ответе я привел пример кода SQL и С#: Pass Dictionary < string, int > к хранимой процедуре T-SQL.
Нет поддержки массива в sql-сервере, но есть несколько способов, с помощью которых вы можете передать коллекцию в сохраненный процесс.
Следующая ссылка может помочь вам
Я просматривал все примеры и ответы о том, как передать любой массив на сервер sql без проблем с созданием нового типа таблицы, пока я не найду этот linK ниже, как я применил его к моему проекту:
- Следующий код собирает массив в качестве параметра и вставляет значения этого --array в другую таблицу
Create Procedure Proc1
@UserId int, //just an Id param
@s nvarchar(max) //this is the array your going to pass from C# code to your Sproc
AS
declare @xml xml
set @xml = N'<root><r>' + replace(@s,',','</r><r>') + '</r></root>'
Insert into UserRole (UserID,RoleID)
select
@UserId [UserId], t.value('.','varchar(max)') as [RoleId]
from @xml.nodes('//root/r') as a(t)
END
Надеюсь, вам понравится
Это поможет вам.:) Следуйте следующим шагам,
Скопировать Вставьте следующий код так, как он есть, он создаст функцию, которая преобразует String в Int
CREATE FUNCTION dbo.SplitInts
(
@List VARCHAR(MAX),
@Delimiter VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT Item = CONVERT(INT, Item) FROM
( SELECT Item = x.i.value('(./text())[1]', 'varchar(max)')
FROM ( SELECT [XML] = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>') + '</i>').query('.')
) AS a CROSS APPLY [XML].nodes('i') AS x(i) ) AS y
WHERE Item IS NOT NULL
);
GO
Создайте следующую хранимую процедуру
CREATE PROCEDURE dbo.sp_DeleteMultipleId
@List VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM TableName WHERE Id IN( SELECT Id = Item FROM dbo.SplitInts(@List, ','));
END
GO
Выполните этот SP с помощью exec sp_DeleteId '1,2,3,12'
, это строка идентификатора, которую вы хотите удалить,
Вы преобразуете свой массив в строку в С# и передаете его как параметр хранимой процедуры
int[] intarray = { 1, 2, 3, 4, 5 };
string[] result = intarray.Select(x=>x.ToString()).ToArray();
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "sp_DeleteMultipleId";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@Id",SqlDbType.VARCHAR).Value=result ;
Это приведет к удалению нескольких строк, всего наилучшего
Мне потребовалось много времени, чтобы понять это, поэтому на случай, если кому-то это понадобится...
Это основано на методе SQL 2005 в ответе Аарона и использовании его функции SplitInts (я просто удалил параметр delim, так как всегда буду использовать запятые). Я использую SQL 2008, но мне нужно что-то, что работает с типизированными наборами данных (XSD, TableAdapters), и я знаю, что строковые параметры работают с ними.
Я пытался заставить свою функцию работать в статье типа "где в (1,2,3)" и не повезло прямолинейно. Поэтому я сначала создал временную таблицу, а затем сделал внутреннее соединение вместо "где в". Вот мой пример использования, в моем случае я хотел получить список рецептов, которые не содержат определенных ингредиентов:
CREATE PROCEDURE dbo.SOExample1
(
@excludeIngredientsString varchar(MAX) = ''
)
AS
/* Convert string to table of ints */
DECLARE @excludeIngredients TABLE (ID int)
insert into @excludeIngredients
select ID = Item from dbo.SplitInts(@excludeIngredientsString)
/* Select recipies that don't contain any ingredients in our excluded table */
SELECT r.Name, r.Slug
FROM Recipes AS r LEFT OUTER JOIN
RecipeIngredients as ri inner join
@excludeIngredients as ei on ri.IngredientID = ei.ID
ON r.ID = ri.RecipeID
WHERE (ri.RecipeID IS NULL)