Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Предайте речник към T-SQL за съхранявана процедура

Приетият отговор за използване на TVP като цяло е правилен, но се нуждае от известно разяснение въз основа на количеството данни, които се предават. Използването на DataTable е добре (да не говорим за бързо и лесно) за по-малки набори от данни, но за по-големи набори е добре не мащабира, като се има предвид, че дублира набора от данни, като го поставя в DataTable просто за средствата за предаването му към SQL Server. Така че за по-големи набори от данни има опция за поточно предаване на съдържанието на всяка персонализирана колекция. Единственото реално изискване е, че трябва да дефинирате структурата от гледна точка на SqlDb типове и да преминете през колекцията, като и двете са доста тривиални стъпки.

По-долу е показан опростен преглед на минималната структура, който е адаптация на отговора, който публикувах в Как мога да вмъкна 10 милиона записа за възможно най-кратко време?, който се занимава с импортиране на данни от файл и следователно е малко по-различен от данните в момента не са в паметта. Както можете да видите от кода по-долу, тази настройка не е прекалено сложна, но е много гъвкава, както и ефективна и мащабируема.

SQL обект № 1:Дефинирайте структурата

-- 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 обект № 2:Използвайте структурата

-- 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#, част 1:Дефиниране на итератора/подателя

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#, част 2:Използвайте итератора/подавателя

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да ограничите редовете в набор от резултати на SQL Server

  2. Как да активирате всички ограничения за проверка и външни ключове в база данни в SQL Server (примери за T-SQL)

  3. Разделете диапазона от време на един ред на месец в sql сървър

  4. списък, разделен със запетая, като единичен низ, T-SQL

  5. Избройте заявките, изпълнявани на SQL Server