sql >> Database >  >> RDS >> Sqlserver

Geef Dictionary door aan de opgeslagen procedure T-SQL

Het geaccepteerde antwoord op het gebruik van een TVP is over het algemeen correct, maar behoeft enige verduidelijking op basis van de hoeveelheid gegevens die wordt doorgegeven. Het gebruik van een DataTable is prima (om nog maar te zwijgen van snel en gemakkelijk) voor kleinere gegevenssets, maar voor grotere sets wel niet geschaald, aangezien het de dataset dupliceert door deze in de DataTable te plaatsen, simpelweg om deze door te geven aan SQL Server. Voor grotere gegevenssets is er dus een optie om de inhoud van een aangepaste verzameling te streamen. De enige echte vereiste is dat je de structuur moet definiëren in termen van SqlDb-typen en de verzameling moet doorlopen, wat beide vrij triviale stappen zijn.

Een simplistisch overzicht van de minimale structuur wordt hieronder weergegeven, wat een aanpassing is van het antwoord dat ik heb gepost op Hoe kan ik 10 miljoen records in de kortst mogelijke tijd invoegen?, dat handelt over het importeren van gegevens uit een bestand en daarom iets anders is als de gegevens bevinden zich momenteel niet in het geheugen. Zoals je kunt zien aan de hand van de onderstaande code, is deze setup niet al te ingewikkeld, maar toch zeer flexibel, maar ook efficiënt en schaalbaar.

SQL-object #1:Definieer de structuur

-- First: You need a User-Defined Table Type
CREATE TYPE dbo.IDsAndOrderNumbers AS TABLE
(
   ID NVARCHAR(4000) NOT NULL,
   SortOrderNumber INT NOT NULL
);
GO

SQL-object #2:Gebruik de structuur

-- Second: Use the UDTT as an input param to an import proc.
--         Hence "Tabled-Valued Parameter" (TVP)
CREATE PROCEDURE dbo.ImportData (
   @ImportTable    dbo.IDsAndOrderNumbers READONLY
)
AS
SET NOCOUNT ON;

-- maybe clear out the table first?
TRUNCATE TABLE SchemaName.TableName;

INSERT INTO SchemaName.TableName (ID, SortOrderNumber)
    SELECT  tmp.ID,
            tmp.SortOrderNumber
    FROM    @ImportTable tmp;

-- OR --

some other T-SQL

-- optional return data
SELECT @NumUpdates AS [RowsUpdated],
       @NumInserts AS [RowsInserted];
GO

C#-code, deel 1:definieer de iterator/afzender

using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using Microsoft.SqlServer.Server;

private static IEnumerable<SqlDataRecord> SendRows(Dictionary<string,int> RowData)
{
   SqlMetaData[] _TvpSchema = new SqlMetaData[] {
      new SqlMetaData("ID", SqlDbType.NVarChar, 4000),
      new SqlMetaData("SortOrderNumber", SqlDbType.Int)
   };
   SqlDataRecord _DataRecord = new SqlDataRecord(_TvpSchema);
   StreamReader _FileReader = null;

      // read a row, send a row
      foreach (KeyValuePair<string,int> _CurrentRow in RowData)
      {
         // You shouldn't need to call "_DataRecord = new SqlDataRecord" as
         // SQL Server already received the row when "yield return" was called.
         // Unlike BCP and BULK INSERT, you have the option here to create an
         // object, do manipulation(s) / validation(s) on the object, then pass
         // the object to the DB or discard via "continue" if invalid.
         _DataRecord.SetString(0, _CurrentRow.ID);
         _DataRecord.SetInt32(1, _CurrentRow.sortOrderNumber);

         yield return _DataRecord;
      }
}

C#-code, deel 2:gebruik de iterator/afzender

public static void LoadData(Dictionary<string,int> MyCollection)
{
   SqlConnection _Connection = new SqlConnection("{connection string}");
   SqlCommand _Command = new SqlCommand("ImportData", _Connection);
   SqlDataReader _Reader = null; // only needed if getting data back from proc call

   SqlParameter _TVParam = new SqlParameter();
   _TVParam.ParameterName = "@ImportTable";
// _TVParam.TypeName = "IDsAndOrderNumbers"; //optional for CommandType.StoredProcedure
   _TVParam.SqlDbType = SqlDbType.Structured;
   _TVParam.Value = SendRows(MyCollection); // method return value is streamed data
   _Command.Parameters.Add(_TVParam);
   _Command.CommandType = CommandType.StoredProcedure;

   try
   {
      _Connection.Open();

      // Either send the data and move on with life:
      _Command.ExecuteNonQuery();
      // OR, to get data back from a SELECT or OUTPUT clause:
      SqlDataReader _Reader = _Command.ExecuteReader();
      {
       Do something with _Reader: If using INSERT or MERGE in the Stored Proc, use an
       OUTPUT clause to return INSERTED.[RowNum], INSERTED.[ID] (where [RowNum] is an
       IDENTITY), then fill a new Dictionary<string, int>(ID, RowNumber) from
       _Reader.GetString(0) and _Reader.GetInt32(1). Return that instead of void.
      }
   }
   finally
   {
      _Reader.Dispose(); // optional; needed if getting data back from proc call
      _Command.Dispose();
      _Connection.Dispose();
   }
}


  1. Alleen nieuwe rijen in bulk invoegen in PostreSQL

  2. Is er een manier om de JBoss-verbindingspool opnieuw te verbinden met Oracle wanneer de verbindingen slecht worden?

  3. PostgreSQL, SQL-status:42601

  4. Hoe de versie van oracle-provider voor ole-db te controleren. OraOLEDB.Oracle-provider