it-swarm.com.de

Übergeben eines Arrays an eine gespeicherte SQL Server-Prozedur

Wie übergebe ich ein Array an eine gespeicherte SQL Server-Prozedur?

Zum Beispiel habe ich eine Liste von Mitarbeitern. Ich möchte diese Liste als Tabelle verwenden und sie mit einer anderen Tabelle verbinden. Die Liste der Mitarbeiter sollte jedoch als Parameter von C # übergeben werden.

263
Sergey

SQL Server 2008 (oder neuer)

Erstellen Sie zunächst in Ihrer Datenbank die folgenden zwei 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 deinem 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 weiterhin eine Split-Funktion ü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 nur 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 nur als '1,2,3,12'... übergeben.


Ich finde, dass die Methode zum Durchlaufen von Tabellenwertparametern die Wartbarkeit einer Lösung vereinfacht, die sie verwendet, und häufig die Leistung im Vergleich zu anderen Implementierungen, einschließlich XML und String-Aufteilung, erhöht.

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 für die gespeicherte Prozedur zu untersuchen.

Im Vergleich zu Lösungen mit benutzerdefiniertem XML-Schema anstelle von UDTs umfasst dies eine ähnliche Anzahl von Schritten, aber meiner Erfahrung nach ist Code viel einfacher zu verwalten, zu warten und zu lesen.

In vielen Lösungen benötigen Sie möglicherweise nur einen oder mehrere 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 zu durchlaufen. Der Funktionsname beschreibt, welchen Kontext diese IDs darstellen sollen. Der Typname sollte generisch sein.

408
Aaron Bertrand

Nach meiner Erfahrung gibt es für dieses Problem eine knifflige und nützliche Lösung, wenn Sie einen durch Trennzeichen getrennten Ausdruck aus den employeeIDs erstellen. Sie sollten nur einen Zeichenfolgenausdruck wie ';123;434;365;' erstellen, in dem 123, 434 und 365 Mitarbeiter-IDs sind. Wenn Sie die folgende Prozedur aufrufen und diesen Ausdruck übergeben, können Sie die gewünschten Datensätze abrufen. Mit Leichtigkeit können Sie die "andere Tabelle" in diese Abfrage einfügen. Diese Lösung eignet sich für alle Versionen von SQL Server. Im Vergleich zu Tabellenvariablen oder temporären Tabellen ist dies eine sehr schnelle und optimierte Lösung.

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
39

Verwenden Sie einen Tabellenwertparameter für Ihre gespeicherte Prozedur.

Wenn Sie es von C # übergeben, fügen Sie den Parameter mit dem Datentyp SqlDb.Structured hinzu.

Siehe hier: http://msdn.Microsoft.com/en-us/library/bb675163.aspx

Beispiel:

// 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();
}
24
Levi W

Sie müssen es als XML-Parameter übergeben.

Edit: schneller Code aus meinem Projekt, um Ihnen eine Idee zu geben:

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)

Dann können Sie die temporäre Tabelle verwenden, um sie mit Ihren Tabellen zu verknüpfen. Wir haben arrayOfUlong als integriertes XML-Schema definiert, um die Datenintegrität zu gewährleisten. Dies ist jedoch nicht erforderlich. Ich würde empfehlen, es zu verwenden. Hier ist ein kurzer Code, um sicherzustellen, dass Sie immer ein XML mit Longs erhalten.

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
15
Fedor Hajdu

Der Kontext ist immer wichtig, z. B. die Größe und Komplexität des Arrays . Für kleine bis mittelgroße Listen sind einige der hier veröffentlichten Antworten in Ordnung, obwohl einige Klarstellungen vorgenommen werden sollten:

  • Zum Teilen einer begrenzten Liste ist ein SQLCLR-basierter Splitter am schnellsten. Es gibt zahlreiche Beispiele, wenn Sie Ihre eigenen schreiben möchten, oder Sie können einfach die kostenlose SQL # - Bibliothek von CLR-Funktionen herunterladen (die ich geschrieben habe, aber die String_Split-Funktion und viele andere sind völlig kostenlos ).
  • Das Aufteilen von XML-basierten Arrays kann schnell sein , Sie müssen jedoch attributbasiertes XML und nicht elementbasiertes XML verwenden (dies ist der einzige in gezeigte Typ) Die Antworten hier, obwohl das XML-Beispiel von @ AaronBertrand das beste ist, da sein Code die XML-Funktion text() verwendet. Weitere Informationen (dh Leistungsanalyse) zur Verwendung von XML zum Teilen von Listen finden Sie unter "Using XML") um Listen als Parameter in SQL Server zu übergeben " by Phil Factor.
  • Die Verwendung von TVPs ist großartig (vorausgesetzt, Sie verwenden mindestens SQL Server 2008 oder höher), da die Daten zum Proc gestreamt werden und als Tabellenvariable voranalysiert und stark typisiert angezeigt werden. In den meisten Fällen bedeutet das Speichern aller Daten in DataTable jedoch das Duplizieren der Daten im Speicher, während sie aus der ursprünglichen Sammlung kopiert werden. Daher funktioniert die Verwendung der DataTable -Methode zum Weiterleiten von TVPs nicht gut für größere Datensätze (d. H. Nicht gut skaliert).
  • XML kann im Gegensatz zu einfachen getrennten Listen von Ints oder Strings mehr als eindimensionale Arrays verarbeiten, genau wie TVPs. Aber auch wie die TVP-Methode DataTable lässt sich XML nicht gut skalieren, da es die Datengröße im Speicher mehr als verdoppelt, da es zusätzlich den Overhead des XML-Dokuments berücksichtigen muss.

WENN die von Ihnen verwendeten Daten groß sind oder noch nicht sehr groß sind, aber stetig wachsen, ist die TVP-Methode IEnumerable die beste Wahl, da sie die Daten an SQL Server (wie die DataTable -Methode), ABER erfordert keine Duplizierung der Sammlung im Speicher (im Gegensatz zu einer der anderen Methoden). Ich habe ein Beispiel für den SQL- und C # -Code in dieser Antwort veröffentlicht:

Dictionary an Stored Procedure T-SQL übergeben

13
Solomon Rutzky

Array wird in SQL Server nicht unterstützt, es gibt jedoch verschiedene Möglichkeiten, wie Sie die Auflistung an einen gespeicherten Prozess übergeben können.

  1. Durch die Verwendung von datatable
  2. Verwenden Sie XML. Konvertieren Sie Ihre Sammlung in ein XML-Format und übergeben Sie sie als Eingabe an eine gespeicherte Prozedur

Der folgende Link kann Ihnen helfen

Übergabe der Auflistung an eine gespeicherte Prozedur

6
praveen

Das wird dir helfen. :) Befolgen Sie die nächsten Schritte,

  1. Öffnen Sie den Abfrage-Designer
  2. Kopieren Füge den folgenden Code so ein, wie er ist, und erstelle die Funktion, die den String in Int konvertiert

    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
    
  3. Erstellen Sie die folgende gespeicherte Prozedur

     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
    
  4. Führe dies aus SP Benutze exec sp_DeleteId '1,2,3,12' Dies ist eine Zeichenkette von IDs, die du löschen möchtest.

  5. Sie konvertieren Ihr Array in C # in eine Zeichenfolge und übergeben es als Parameter für gespeicherte Prozeduren

    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 ;
    

Dadurch werden mehrere Zeilen gelöscht. Alles Gute

5
Charan Ghate

Ich habe alle Beispiele und Antworten durchsucht, wie ein Array an einen SQL-Server übergeben werden kann, ohne dass ein neuer Tabellentyp erstellt werden muss, bis ich diesen Link gefunden habe , so habe ich es auf mein Projekt angewendet:

--Der folgende Code ruft ein Array als Parameter ab und fügt die Werte dieses --arrays in eine andere Tabelle ein

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 

Hoffe es gefällt euch

5
Adam

Ich habe lange gebraucht, um das herauszufinden, falls es jemand braucht ...

Dies basiert auf der SQL 2005-Methode in Aarons Antwort und der Verwendung seiner SplitInts-Funktion (ich habe gerade den delim-Parameter entfernt, da ich immer Kommas verwenden werde). Ich verwende SQL 2008, aber ich wollte etwas, das mit typisierten Datasets (XSD, TableAdapters) funktioniert, und ich weiß, dass String-Parameter mit diesen funktionieren.

Ich habe versucht, seine Funktion dazu zu bringen, in einer Klausel vom Typ "where in (1,2,3)" zu arbeiten, und auf einfache Weise kein Glück zu haben. Also habe ich zuerst eine temporäre Tabelle erstellt und dann einen Inner Join anstelle des "Where In" durchgeführt. Hier ist mein Anwendungsbeispiel, in meinem Fall wollte ich eine Liste von Rezepten erhalten, die bestimmte Zutaten nicht enthalten:

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