Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Làm cách nào để tôi có thể chèn 10 triệu bản ghi trong thời gian ngắn nhất có thể?

Vui lòng không không tạo một DataTable để tải qua BulkCopy. Đó là một giải pháp phù hợp cho các tập dữ liệu nhỏ hơn, nhưng hoàn toàn không có lý do gì để tải tất cả 10 triệu hàng vào bộ nhớ trước khi gọi cơ sở dữ liệu.

Đặt cược tốt nhất của bạn (ngoài BCP / BULK INSERT / OPENROWSET(BULK...) ) là truyền nội dung từ tệp vào cơ sở dữ liệu thông qua Tham số giá trị bảng (TVP). Bằng cách sử dụng TVP, bạn có thể mở tệp, đọc một hàng và gửi một hàng cho đến khi hoàn tất, sau đó đóng tệp. Phương thức này có vùng nhớ chỉ là một hàng. Tôi đã viết một bài báo, Truyền dữ liệu vào SQL Server 2008 từ một ứng dụng, trong đó có một ví dụ về tình huống này.

Tổng quan đơn giản về cấu trúc như sau. Tôi giả định rằng bảng nhập và tên trường giống như được hiển thị trong câu hỏi ở trên.

Các đối tượng cơ sở dữ liệu bắt buộc:

-- 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

Dưới đây là mã ứng dụng C # để sử dụng các đối tượng SQL ở trên. Lưu ý cách thay vì điền vào một đối tượng (ví dụ:DataTable) và sau đó thực hiện Thủ tục được lưu trữ, trong phương pháp này, việc thực thi Thủ tục được lưu trữ sẽ bắt đầu đọc nội dung tệp. Tham số đầu vào của Stored Proc không phải là một biến; nó là giá trị trả về của một phương thức, GetFileContents . Phương thức đó được gọi khi SqlCommand cuộc gọi ExecuteNonQuery , mở tệp, đọc một hàng và gửi hàng đó đến SQL Server qua IEnumerable<SqlDataRecord>yield return cấu trúc, và sau đó đóng tệp. Thủ tục đã lưu trữ chỉ thấy một Biến bảng, @ImportTable, có thể được truy cập ngay sau khi dữ liệu bắt đầu chuyển sang ( lưu ý:dữ liệu vẫn tồn tại trong một thời gian ngắn, ngay cả khi không phải là toàn bộ nội dung, trong 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 phương thức trên được sử dụng làm giá trị tham số đầu vào cho Thủ tục được lưu trữ như được hiển thị bên dưới:

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;
}

Ghi chú bổ sung:

  1. Với một số sửa đổi, mã C # ở trên có thể được điều chỉnh để phù hợp với dữ liệu.
  2. Với sửa đổi nhỏ, mã C # ở trên có thể được điều chỉnh để gửi trong nhiều trường (ví dụ được hiển thị trong bài viết "Dữ liệu hấp ..." được liên kết ở trên chuyển sang 2 trường).
  3. Bạn cũng có thể thao tác giá trị của từng bản ghi trong SELECT tuyên bố trong chương trình.
  4. Bạn cũng có thể lọc ra các hàng bằng cách sử dụng điều kiện WHERE trong chương trình.
  5. Bạn có thể truy cập Biến Bảng TVP nhiều lần; nó là SN SÀNG nhưng không phải là "chỉ chuyển tiếp".
  6. Ưu điểm so với SqlBulkCopy :
    1. SqlBulkCopy là chỉ INSERT trong khi sử dụng TVP cho phép dữ liệu được sử dụng theo bất kỳ cách nào:bạn có thể gọi MERGE; bạn có thể DELETE dựa trên một số điều kiện; bạn có thể chia dữ liệu thành nhiều bảng; vân vân.
    2. Do TVP không chỉ ở chế độ CHÈN, bạn không cần một bảng dàn dựng riêng để kết xuất dữ liệu vào.
    3. Bạn có thể lấy lại dữ liệu từ cơ sở dữ liệu bằng cách gọi ExecuteReader thay vì ExecuteNonQuery . Ví dụ:nếu có IDENTITY trên trường DATAs nhập bảng, bạn có thể thêm một OUTPUT mệnh đề INSERT để trả lại INSERTED.[ID] (giả sử ID là tên của IDENTITY đồng ruộng). Hoặc bạn có thể trả lại kết quả của một truy vấn hoàn toàn khác hoặc cả hai vì nhiều bộ kết quả có thể được gửi và truy cập thông qua Reader.NextResult() . Không thể lấy lại thông tin từ cơ sở dữ liệu khi sử dụng SqlBulkCopy Tuy nhiên, có một số câu hỏi ở đây trên S.O. những người muốn làm chính xác điều đó (ít nhất là liên quan đến IDENTITY mới được tạo giá trị).
    4. Để biết thêm thông tin về lý do tại sao quá trình tổng thể đôi khi nhanh hơn, ngay cả khi tải dữ liệu từ đĩa vào SQL Server chậm hơn một chút, vui lòng xem báo cáo chính thức này từ Nhóm tư vấn khách hàng của SQL Server:Tối đa hóa thông lượng với 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. làm cách nào để cập nhật 100 bản ghi hàng đầu trong máy chủ sql

  2. 2 cách lấy ngôn ngữ mặc định của đăng nhập trong SQL Server (T-SQL)

  3. Cách lấy Bảng cha, Bảng tham chiếu, Tên và cột ràng buộc khóa ngoại trong SQL Server - Hướng dẫn SQL Server / TSQL Phần 71

  4. Tôi có thể sử dụng Kiểu dữ liệu ngày trong máy chủ sql như thế nào?

  5. Chèn tất cả các giá trị của một bảng vào một bảng khác trong SQL