SQL Server 2016 (oder neuer)
Sie können eine abgegrenzte Liste oder JSON
und verwenden STRING_SPLIT()
o OPENJSON()
.
STRING_SPLIT()
:
CREATE PROCEDURE dbo.DoSomethingWithEmployees
@List varchar(max)
AS
BEGIN
SET NOCOUNT ON;
SELECT value FROM STRING_SPLIT(@List, ',');
END
GO
EXEC dbo.DoSomethingWithEmployees @List = '1,2,3';
OPENJSON()
:
CREATE PROCEDURE dbo.DoSomethingWithEmployees
@List varchar(max)
AS
BEGIN
SET NOCOUNT ON;
SELECT value FROM OPENJSON(CONCAT('["',
REPLACE(STRING_ESCAPE(@List, 'JSON'),
',', '","'), '"]')) AS j;
END
GO
EXEC dbo.DoSomethingWithEmployees @List = '1,2,3';
Mehr dazu habe ich hier geschrieben:
SQL Server 2008 (oder neuer)
Erstellen Sie zunächst in Ihrer Datenbank die folgenden beiden Objekte:
CREATE TYPE dbo.IDList
AS TABLE
(
ID INT
);
GO
CREATE PROCEDURE dbo.DoSomethingWithEmployees
@List AS dbo.IDList READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT ID FROM @List;
END
GO
Jetzt in Ihrem C#-Code:
// Obtain your list of ids to send, this is just an example call to a helper utility function
int[] employeeIds = GetEmployeeIds();
DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("ID", typeof(int)));
// populate DataTable from your List here
foreach(var id in employeeIds)
tvp.Rows.Add(id);
using (conn)
{
SqlCommand cmd = new SqlCommand("dbo.DoSomethingWithEmployees", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvparam = cmd.Parameters.AddWithValue("@List", tvp);
// these next lines are important to map the C# DataTable object to the correct SQL User Defined Type
tvparam.SqlDbType = SqlDbType.Structured;
tvparam.TypeName = "dbo.IDList";
// execute query, consume results, etc. here
}
SQL Server 2005
Wenn Sie SQL Server 2005 verwenden, würde ich trotzdem eine Split-Funktion gegenüber XML empfehlen. Erstellen Sie zunächst eine Funktion:
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
Jetzt kann Ihre gespeicherte Prozedur einfach sein:
CREATE PROCEDURE dbo.DoSomethingWithEmployees
@List VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
SELECT EmployeeID = Item FROM dbo.SplitInts(@List, ',');
END
GO
Und in Ihrem C#-Code müssen Sie die Liste einfach als '1,2,3,12'
...
Ich finde, dass die Methode der Übergabe von tabellenwertigen Parametern die Wartbarkeit einer Lösung, die sie verwendet, vereinfacht und oft eine höhere Leistung im Vergleich zu anderen Implementierungen, einschließlich XML und String-Splitting, aufweist.
Die Eingaben sind klar definiert (niemand muss raten, ob das Trennzeichen ein Komma oder ein Semikolon ist), und wir haben keine Abhängigkeiten von anderen Verarbeitungsfunktionen, die nicht offensichtlich sind, ohne den Code der gespeicherten Prozedur zu inspizieren.
Im Vergleich zu Lösungen, die benutzerdefinierte XML-Schemata anstelle von UDTs verwenden, umfasst dies eine ähnliche Anzahl von Schritten, aber meiner Erfahrung nach ist der Code viel einfacher zu verwalten, zu pflegen und zu lesen.
In vielen Lösungen benötigen Sie vielleicht nur einen oder einige wenige dieser UDTs (User defined Types), die Sie für viele gespeicherte Prozeduren wiederverwenden. Wie in diesem Beispiel besteht die allgemeine Anforderung darin, eine Liste von ID-Zeigern durchzugeben, der Funktionsname beschreibt, welchen Kontext diese Ids darstellen sollen, der Typname sollte generisch sein.