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

Как мога да вмъкна 10 милиона записа за възможно най-кратко време?

Моля, не създайте DataTable за зареждане чрез BulkCopy. Това е добро решение за по-малки набори от данни, но няма абсолютно никаква причина да зареждате всичките 10 милиона реда в паметта, преди да извикате базата данни.

Най-добрият ви залог (извън BCP / BULK INSERT / OPENROWSET(BULK...) ) е да предава поточно съдържанието от файла в базата данни чрез параметър с таблица с стойност (TVP). С помощта на TVP можете да отворите файла, да прочетете ред и да изпратите ред, докато приключите, и след това да затворите файла. Този метод има отпечатък на паметта само от един ред. Написах статия, Поточно предаване на данни в SQL Server 2008 от приложение, в която има пример за точно този сценарий.

Опростеният преглед на структурата е както следва. Предполагам същата таблица за импортиране и име на поле, както е показано във въпроса по-горе.

Необходими обекти на базата данни:

-- First: You need a User-Defined Table Type
CREATE TYPE ImportStructure AS TABLE (Field VARCHAR(MAX));
GO

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

-- maybe clear out the table first?
TRUNCATE TABLE dbo.DATAs;

INSERT INTO dbo.DATAs (DatasField)
    SELECT  Field
    FROM    @ImportTable;

GO

Кодът на приложението C# за използване на горните SQL обекти е по-долу. Забележете как вместо да попълвате обект (напр. DataTable) и след това да изпълнявате съхранената процедура, в този метод изпълнението на съхранената процедура инициира четенето на съдържанието на файла. Входният параметър на Stored Proc не е променлива; това е върнатата стойност на метод, GetFileContents . Този метод се извиква, когато SqlCommand извиква ExecuteNonQuery , който отваря файла, чете ред и изпраща реда до SQL Server чрез IEnumerable<SqlDataRecord> и yield return изгражда и след това затваря файла. Съхранената процедура просто вижда променлива на таблица, @ImportTable, която може да бъде достъпна веднага щом данните започнат да идват (забележка:данните се запазват за кратко време, дори ако не е пълното съдържание, в tempdb ).

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

private static IEnumerable<SqlDataRecord> GetFileContents()
{
   SqlMetaData[] _TvpSchema = new SqlMetaData[] {
      new SqlMetaData("Field", SqlDbType.VarChar, SqlMetaData.Max)
   };
   SqlDataRecord _DataRecord = new SqlDataRecord(_TvpSchema);
   StreamReader _FileReader = null;

   try
   {
      _FileReader = new StreamReader("{filePath}");

      // read a row, send a row
      while (!_FileReader.EndOfStream)
      {
         // 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 a string
         // call ReadLine() into the string, do manipulation(s) / validation(s) on
         // the string, then pass that string into SetString() or discard if invalid.
         _DataRecord.SetString(0, _FileReader.ReadLine());
         yield return _DataRecord;
      }
   }
   finally
   {
      _FileReader.Close();
   }
}

GetFileContents методът по-горе се използва като стойност на входния параметър за съхранената процедура, както е показано по-долу:

public static void test()
{
   SqlConnection _Connection = new SqlConnection("{connection string}");
   SqlCommand _Command = new SqlCommand("ImportData", _Connection);
   _Command.CommandType = CommandType.StoredProcedure;

   SqlParameter _TVParam = new SqlParameter();
   _TVParam.ParameterName = "@ImportTable";
   _TVParam.TypeName = "dbo.ImportStructure";
   _TVParam.SqlDbType = SqlDbType.Structured;
   _TVParam.Value = GetFileContents(); // return value of the method is streamed data
   _Command.Parameters.Add(_TVParam);

   try
   {
      _Connection.Open();

      _Command.ExecuteNonQuery();
   }
   finally
   {
      _Connection.Close();
   }

   return;
}

Допълнителни бележки:

  1. С известна модификация горният C# код може да бъде адаптиран, за да пакетира данните.
  2. С незначителна модификация горният C# код може да бъде адаптиран за изпращане в множество полета (примерът, показан в статията „Данни на пара...“, свързана по-горе, преминава в 2 полета).
  3. Можете също да манипулирате стойността на всеки запис в SELECT изявление в процедурата.
  4. Можете също да филтрирате редове, като използвате условие WHERE в процедурата.
  5. Можете да получите достъп до променливата на таблицата TVP няколко пъти; той е САМО ЧЕТЕНЕ, но не и "само препращане".
  6. Предимства пред SqlBulkCopy :
    1. SqlBulkCopy е само INSERT, докато използването на TVP позволява данните да се използват по всякакъв начин:можете да извикате MERGE; можете да DELETE въз основа на някакво условие; можете да разделите данните на множество таблици; и така нататък.
    2. Поради това, че TVP не е само INSERT, нямате нужда от отделна таблица за етапи, в която да изхвърляте данните.
    3. Можете да получите обратно данни от базата данни, като извикате ExecuteReader вместо ExecuteNonQuery . Например, ако имаше IDENTITY полето в DATAs таблица за импортиране, можете да добавите OUTPUT клауза към INSERT за да предаде обратно INSERTED.[ID] (приемайки ID е името на IDENTITY поле). Или можете да изпратите обратно резултатите от напълно различна заявка или и двете, тъй като множество набори от резултати могат да бъдат изпратени и достъпни чрез Reader.NextResult() . Получаването на информация от базата данни не е възможно при използване на SqlBulkCopy все пак има няколко въпроса тук относно S.O. от хората, които искат да направят точно това (поне по отношение на новосъздадения IDENTITY стойности).
    4. За повече информация защо понякога е по-бърз за цялостния процес, дори и малко по-бавен при получаването на данните от диска в SQL Server, моля, вижте тази бяла книга от екипа за консултации на клиенти на SQL Server:Максимизиране на производителността с TVP


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SYSDATETIME() Примери в SQL Server (T-SQL)

  2. Вземете всички дати между две дати в SQL Server

  3. Как да премахнете водещото празно пространство в SQL Server – LTRIM()

  4. Вътрешни елементи за репликация на транзакции на SQL Server

  5. При каскада за изтриване за таблица за саморефериране