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

Chèn hàng loạt tệp dữ liệu vào máy chủ SQL

Theo Wikipedia, chèn hàng loạt là một quy trình hoặc phương pháp được cung cấp bởi hệ thống quản lý cơ sở dữ liệu để tải nhiều hàng dữ liệu vào một bảng cơ sở dữ liệu. Nếu chúng tôi điều chỉnh phần giải thích này thành câu lệnh BULK INSERT, phần chèn hàng loạt cho phép nhập các tệp dữ liệu bên ngoài vào SQL Server.

Giả sử rằng tổ chức của chúng tôi có tệp CSV gồm 1.500.000 hàng và chúng tôi muốn nhập tệp đó vào một bảng cụ thể trong SQL Server để sử dụng câu lệnh BULK INSERT trong SQL Server. Chúng tôi có thể tìm thấy một số phương pháp để xử lý nhiệm vụ này. Nó có thể đang sử dụng BCP ( b ulk c opy p rogram), Trình hướng dẫn Nhập và Xuất SQL Server hoặc gói Dịch vụ Tích hợp Máy chủ SQL. Tuy nhiên, câu lệnh BULK INSERT nhanh hơn và mạnh hơn nhiều. Một ưu điểm khác là nó cung cấp một số tham số giúp xác định cài đặt quy trình chèn hàng loạt.

Hãy bắt đầu với một mẫu cơ bản. Sau đó, chúng ta sẽ đi qua các tình huống phức tạp hơn.

Chuẩn bị

Trước hết, chúng ta cần một tệp CSV mẫu. Chúng tôi tải xuống tệp CSV mẫu từ trang web E for Excel (tập hợp các tệp CSV mẫu có số hàng khác nhau). Ở đây, chúng tôi sẽ sử dụng 1.500.000 Hồ sơ bán hàng.

Tải xuống tệp zip, giải nén để có tệp CSV và đặt tệp đó vào ổ đĩa cục bộ của bạn.

Nhập tệp CSV vào bảng SQL Server

Chúng tôi nhập tệp CSV của mình vào bảng đích ở dạng đơn giản nhất. Tôi đã đặt tệp CSV mẫu của mình trên ổ C:. Bây giờ chúng ta tạo một bảng để nhập dữ liệu tệp CSV vào đó:

DROP TABLE IF EXISTS Sales 

CREATE TABLE [dbo].[Sales](
	[Region] [varchar](50) ,
	[Country] [varchar](50) ,
	[ItemType] [varchar](50) NULL,
	[SalesChannel] [varchar](50) NULL,
	[OrderPriority] [varchar](50) NULL,
	[OrderDate]  datetime,
	[OrderID] bigint NULL,
	[ShipDate] datetime,
	[UnitsSold]  float,
	[UnitPrice] float,
	[UnitCost] float,
	[TotalRevenue] float,
	[TotalCost]  float,
	[TotalProfit] float
)

Câu lệnh BULK INSERT sau đây nhập tệp CSV vào bảng Bán hàng:

BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n' );

Bạn có thể đã lưu ý đến các thông số cụ thể của câu lệnh chèn hàng loạt ở trên. Hãy làm rõ chúng:

  • ĐẦU TIÊN xác định điểm bắt đầu của câu lệnh chèn. Trong ví dụ dưới đây, chúng tôi muốn bỏ qua tiêu đề cột, vì vậy chúng tôi đặt thông số này thành 2.

  • FIELDTERMINATOR xác định ký tự ngăn cách các trường với nhau. SQL Server phát hiện từng trường theo cách này.
  • ROWTERMINATOR không khác nhiều so với FIELDTERMINATOR. Nó xác định ký tự phân tách của các hàng.

Trong tệp CSV mẫu, FIELDTERMINATOR rất rõ ràng và nó là dấu phẩy (,). Để phát hiện tham số này, hãy mở tệp CSV trong Notepad ++ và điều hướng đến Chế độ xem -> Hiển thị biểu tượng -> Hiển thị tất cả biểu đồ. Các ký tự CRLF ở cuối mỗi trường.

CR =Vận chuyển hàng trở lại và LF =Nguồn cấp hàng. Chúng được sử dụng để đánh dấu ngắt dòng trong tệp văn bản. Chỉ báo là “\ n” trong câu lệnh chèn hàng loạt.

Một cách khác để nhập tệp CSV vào bảng có chèn hàng loạt là sử dụng tham số FORMAT. Lưu ý rằng tham số này chỉ có sẵn trong SQL Server 2017 và các phiên bản mới hơn.

BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FORMAT='CSV' , FIRSTROW = 2);

Đó là tình huống đơn giản nhất trong đó bảng đích và tệp CSV có số cột bằng nhau. Tuy nhiên, trường hợp bảng đích có nhiều cột hơn thì tệp CSV là điển hình. Hãy xem xét nó.

Chúng tôi thêm khóa chính vào bảng Bán hàng để phá vỡ ánh xạ cột bằng nhau. Chúng tôi tạo bảng Bán hàng bằng khóa chính và nhập tệp CSV thông qua lệnh chèn hàng loạt.

DROP TABLE IF EXISTS Sales 

CREATE TABLE [dbo].[Sales](
    Id INT PRIMARY KEY IDENTITY (1,1),
	[Region] [varchar](50) ,
	[Country] [varchar](50) ,
	[ItemType] [varchar](50) NULL,
	[SalesChannel] [varchar](50) NULL,
	[OrderPriority] [varchar](50) NULL,
	[OrderDate]  datetime,
	[OrderID] bigint NULL,
	[ShipDate] datetime,
	[UnitsSold]  float,
	[UnitPrice] float,
	[UnitCost] float,
	[TotalRevenue] float,
	[TotalCost]  float,
	[TotalProfit] float
) 

BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n' );

Nhưng nó tạo ra một lỗi:

Để khắc phục lỗi, chúng tôi tạo chế độ xem bảng Bán hàng với các cột ánh xạ tới tệp CSV. Sau đó, chúng tôi nhập dữ liệu CSV qua chế độ xem này vào bảng Bán hàng:

DROP VIEW IF EXISTS VSales 
GO
CREATE VIEW VSales
AS
SELECT Region ,
Country ,
ItemType ,
SalesChannel , 
OrderPriority ,
OrderDate , 
OrderID ,
ShipDate ,
UnitsSold , 
UnitPrice ,
UnitCost ,
TotalRevenue,
TotalCost, 
TotalProfit from Sales

GO 

BULK INSERT VSales
FROM 'C:\1500000 Sales Records.csv'
WITH ( FIRSTROW = 2,
       FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n' );

Tách và tải tệp CSV lớn thành kích thước hàng loạt nhỏ

SQL Server có được một khóa đối với bảng đích trong quá trình chèn hàng loạt. Theo mặc định, nếu bạn không đặt tham số BATCHSIZE, SQL Server sẽ mở một giao dịch và chèn toàn bộ dữ liệu CSV vào đó. Với tham số này, SQL Server phân chia dữ liệu CSV theo giá trị tham số.

Hãy chia toàn bộ dữ liệu CSV thành nhiều bộ 300.000 hàng, mỗi bộ.

DROP TABLE IF EXISTS Sales 

CREATE TABLE [dbo].[Sales](
	[Region] [varchar](50) ,
	[Country] [varchar](50) ,
	[ItemType] [varchar](50) NULL,
	[SalesChannel] [varchar](50) NULL,
	[OrderPriority] [varchar](50) NULL,
	[OrderDate]  datetime,
	[OrderID] bigint NULL,
	[ShipDate] datetime,
	[UnitsSold]  float,
	[UnitPrice] float,
	[UnitCost] float,
	[TotalRevenue] float,
	[TotalCost]  float,
	[TotalProfit] float
) 


BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n' ,
	batchsize=300000 );

Dữ liệu sẽ được nhập năm lần theo từng phần.

  • Nếu câu lệnh chèn hàng loạt của bạn không bao gồm tham số BATCHSIZE, thì sẽ xảy ra lỗi và Máy chủ SQL sẽ khôi phục toàn bộ quá trình chèn hàng loạt.
  • Với tham số này được đặt thành câu lệnh chèn hàng loạt, SQL Server chỉ quay lại phần đã xảy ra lỗi.

Không có giá trị tối ưu hoặc tốt nhất cho tham số này vì giá trị của nó có thể thay đổi theo yêu cầu hệ thống cơ sở dữ liệu của bạn.

Đặt hành vi trong trường hợp có lỗi

Nếu lỗi xảy ra trong một số trường hợp sao chép hàng loạt, chúng tôi có thể hủy quá trình sao chép hàng loạt hoặc tiếp tục. Tham số MAXERRORS cho phép chúng tôi chỉ định số lỗi tối đa. Nếu quá trình chèn hàng loạt đạt đến giá trị lỗi tối đa này, quá trình này sẽ hủy hoạt động nhập hàng loạt và quay trở lại. Giá trị mặc định cho tham số này là 10.

Ví dụ:chúng tôi có các loại dữ liệu bị hỏng trong 3 hàng của tệp CSV. Tham số MAXERRORS được đặt thành 2.

DROP TABLE IF EXISTS Sales 
CREATE TABLE [dbo].[Sales](
	[Region] [varchar](50) ,
	[Country] [varchar](50) ,
	[ItemType] [varchar](50) NULL,
	[SalesChannel] [varchar](50) NULL,
	[OrderPriority] [varchar](50) NULL,
	[Order Date]  datetime,
	[OrderID] bigint NULL,
	[ShipDate] datetime,
	[UnitsSold]  float,
	[UnitPrice] float,
	[UnitCost] float,
	[TotalRevenue] float,
	[TotalCost]  float,
	[TotalProfit] float
) 

BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n' ,
	 MAXERRORS=2);

Toàn bộ hoạt động chèn hàng loạt sẽ bị hủy vì có nhiều lỗi hơn giá trị thông số MAXERRORS.

Nếu chúng tôi thay đổi tham số MAXERRORS thành 4, câu lệnh chèn số lượng lớn sẽ bỏ qua các hàng có lỗi này và chèn các hàng có cấu trúc dữ liệu chính xác. Quá trình chèn hàng loạt sẽ hoàn tất.

DROP TABLE IF EXISTS Sales 
CREATE TABLE [dbo].[Sales](
	[Region] [varchar](50) ,
	[Country] [varchar](50) ,
	[ItemType] [varchar](50) NULL,
	[SalesChannel] [varchar](50) NULL,
	[OrderPriority] [varchar](50) NULL,
	[Order Date]  datetime,
	[OrderID] bigint NULL,
	[ShipDate] datetime,
	[UnitsSold]  float,
	[UnitPrice] float,
	[UnitCost] float,
	[TotalRevenue] float,
	[TotalCost]  float,
	[TotalProfit] float
) 
BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n' ,
	 MAXERRORS=4);
	 
	 
	 
SELECT COUNT(*) AS [NumberofImportedRow] FROM Sales

Nếu chúng tôi sử dụng đồng thời cả BATCHSIZE và MAXERRORS, quá trình sao chép hàng loạt sẽ không hủy bỏ toàn bộ hoạt động chèn. Nó sẽ chỉ hủy bỏ phần đã chia.

DROP TABLE IF EXISTS Sales 

CREATE TABLE [dbo].[Sales](
	[Region] [varchar](50) ,
	[Country] [varchar](50) ,
	[ItemType] [varchar](50) NULL,
	[SalesChannel] [varchar](50) NULL,
	[OrderPriority] [varchar](50) NULL,
	[Order Date]  datetime,
	[OrderID] bigint NULL,
	[ShipDate] datetime,
	[UnitsSold]  float,
	[UnitPrice] float,
	[UnitCost] float,
	[TotalRevenue] float,
	[TotalCost]  float,
	[TotalProfit] float
) 
BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n' ,
	 MAXERRORS=2,
BATCHSIZE=750000);
GO 
SELECT COUNT(*) AS [NumberofImportedRow] FROM Sales

Hãy xem hình ảnh bên dưới hiển thị kết quả thực thi tập lệnh:

Các tùy chọn khác của quy trình chèn hàng loạt

FIRE_TRIGGERS - bật trình kích hoạt trong bảng đích trong quá trình chèn hàng loạt

Theo mặc định, trong quá trình chèn hàng loạt, các trình kích hoạt chèn được chỉ định trong bảng đích không được kích hoạt. Tuy nhiên, trong một số trường hợp, chúng tôi có thể muốn bật chúng.

Giải pháp là sử dụng tùy chọn FIRE_TRIGGERS trong các câu lệnh chèn hàng loạt. Nhưng xin lưu ý rằng nó có thể ảnh hưởng và làm giảm hiệu suất hoạt động chèn hàng loạt. Đó là vì trình kích hoạt / trình kích hoạt có thể thực hiện các hoạt động riêng biệt trong cơ sở dữ liệu.

Lúc đầu, chúng tôi không đặt thông số FIRE_TRIGGERS và quá trình chèn hàng loạt sẽ không kích hoạt trình kích hoạt chèn. Xem tập lệnh T-SQL bên dưới:

DROP TABLE IF EXISTS Sales 

CREATE TABLE [dbo].[Sales](
	[Region] [varchar](50) ,
	[Country] [varchar](50) ,
	[ItemType] [varchar](50) NULL,
	[SalesChannel] [varchar](50) NULL,
	[OrderPriority] [varchar](50) NULL,
	[OrderDate]  datetime,
	[OrderID] bigint NULL,
	[ShipDate] datetime,
	[UnitsSold]  float,
	[UnitPrice] float,
	[UnitCost] float,
	[TotalRevenue] float,
	[TotalCost]  float,
	[TotalProfit] float
) 
DROP TABLE IF EXISTS SalesLog
CREATE TABLE SalesLog (OrderIDLog bigint)
GO
CREATE TRIGGER OrderLogIns ON Sales
FOR INSERT
AS
BEGIN 
SET NOCOUNT ON 
INSERT INTO SalesLog
SELECT OrderId from inserted
end

GO
BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n'
	 );


	SELECT Count(*) FROM SalesLog

Khi tập lệnh này thực thi, trình kích hoạt chèn sẽ không kích hoạt vì tùy chọn FIRE_TRIGGERS không được đặt.

Bây giờ, hãy thêm tùy chọn FIRE_TRIGGERS vào câu lệnh chèn hàng loạt:

BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2, 
FIELDTERMINATOR = ',', 
ROWTERMINATOR='\n',
FIRE_TRIGGERS);
GO
SELECT Count(*) as [NumberOfRowsinTriggerTable] FROM SalesLog 

CHECK_CONSTRAINTS - bật ràng buộc kiểm tra trong quá trình chèn hàng loạt

Kiểm tra các ràng buộc cho phép chúng tôi thực thi tính toàn vẹn của dữ liệu trong các bảng SQL Server. Mục đích của ràng buộc là để kiểm tra các giá trị được chèn, cập nhật hoặc xóa theo quy định về cú pháp của chúng. Chẳng hạn như, ràng buộc NOT NULL cung cấp rằng giá trị NULL không thể sửa đổi một cột được chỉ định.

Ở đây, chúng tôi tập trung vào các ràng buộc và tương tác chèn hàng loạt. Theo mặc định, trong quá trình chèn hàng loạt, mọi ràng buộc kiểm tra và khóa ngoại đều bị bỏ qua. Nhưng có một số trường hợp ngoại lệ.

Theo Microsoft, các ràng buộc “ UNIQUE và PRIMARY KEY luôn được thực thi. Khi nhập vào cột ký tự mà ràng buộc NOT NULL được xác định, BULK INSERT sẽ chèn một chuỗi trống khi không có giá trị nào trong tệp văn bản. ”

Trong tập lệnh T-SQL sau, chúng tôi thêm một ràng buộc kiểm tra vào cột OrderDate, cột này kiểm soát ngày đặt hàng lớn hơn 01.01.2016.

DROP TABLE IF EXISTS Sales 

CREATE TABLE [dbo].[Sales](
	[Region] [varchar](50) ,
	[Country] [varchar](50) ,
	[ItemType] [varchar](50) NULL,
	[SalesChannel] [varchar](50) NULL,
	[OrderPriority] [varchar](50) NULL,
	[OrderDate]  datetime,
	[OrderID] bigint NULL,
	[ShipDate] datetime,
	[UnitsSold]  float,
	[UnitPrice] float,
	[UnitCost] float,
	[TotalRevenue] float,
	[TotalCost]  float,
	[TotalProfit] float
) 
ALTER TABLE [Sales] ADD CONSTRAINT OrderDate_Check
CHECK(OrderDate >'20160101')

BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n'
	 );
GO
SELECT COUNT(*) AS [UnChekedData] FROM 
Sales WHERE OrderDate <'20160101'

Kết quả là, quá trình chèn hàng loạt bỏ qua kiểm soát ràng buộc kiểm tra. Tuy nhiên, SQL Server chỉ ra ràng buộc kiểm tra là không đáng tin cậy:

SELECT is_not_trusted ,* FROM sys.check_constraints where name='OrderDate_Check'

Giá trị này chỉ ra rằng ai đó đã chèn hoặc cập nhật một số dữ liệu vào cột này bằng cách bỏ qua ràng buộc kiểm tra. Đồng thời, cột này có thể chứa dữ liệu không nhất quán liên quan đến ràng buộc đó.

Cố gắng thực thi câu lệnh chèn hàng loạt với tùy chọn CHECK_CONSTRAINTS. Kết quả rất đơn giản:kiểm tra ràng buộc trả về lỗi do dữ liệu không phù hợp.

TABLOCK - tăng hiệu suất bằng nhiều lần chèn hàng loạt vào một bảng đích

Mục đích chính của cơ chế khóa trong SQL Server là để bảo vệ và đảm bảo tính toàn vẹn của dữ liệu. Trong khái niệm Chính của bài viết về khóa SQL Server, bạn có thể tìm thấy chi tiết về cơ chế khóa.

Chúng tôi sẽ tập trung vào chi tiết khóa quy trình chèn hàng loạt.

Nếu bạn chạy câu lệnh chèn số lượng lớn mà không có tùy chọn TABLELOCK, nó sẽ có được khóa các hàng hoặc bảng theo phân cấp khóa. Nhưng trong một số trường hợp, chúng tôi có thể muốn thực hiện nhiều quy trình chèn hàng loạt vào một bảng đích và do đó giảm thời gian hoạt động.

Đầu tiên, chúng tôi thực hiện đồng thời hai câu lệnh chèn số lượng lớn và phân tích hành vi của cơ chế khóa. Mở hai cửa sổ truy vấn trong SQL Server Management Studio và chạy đồng thời các câu lệnh chèn hàng loạt sau.

BULK INSERT Sales
FROM 'C:\1500000 Sales Records.csv'
WITH (FIRSTROW = 2,
    FIELDTERMINATOR = ',',
	ROWTERMINATOR='\n'
	 );

Thực thi truy vấn DMV (Chế độ xem quản lý động) sau - nó giúp theo dõi trạng thái quy trình chèn hàng loạt:

SELECT session_id,command ,status,last_wait_type,text FROM sys.dm_exec_requests cross apply 
sys.dm_exec_sql_text(sys.dm_exec_requests.sql_handle)
where text like '%BULK INSERT Sales%' and session_id <>@@SPID

Như bạn có thể thấy trong hình trên, phiên 61, trạng thái quá trình chèn hàng loạt bị tạm dừng do khóa. Nếu chúng tôi xác minh sự cố, phiên 59 sẽ khóa bảng đích chèn hàng loạt. Sau đó, phiên 61 chờ giải phóng khóa này để tiếp tục quá trình chèn hàng loạt.

Bây giờ, chúng tôi thêm tùy chọn TABLOCK vào các câu lệnh chèn hàng loạt và thực thi các truy vấn.

Khi chúng tôi thực thi lại truy vấn giám sát DMV, chúng tôi không thể thấy bất kỳ quá trình chèn hàng loạt bị tạm dừng nào vì SQL Server sử dụng một loại khóa cụ thể được gọi là khóa cập nhật hàng loạt (BU). Loại khóa này cho phép xử lý đồng thời nhiều thao tác chèn hàng loạt vào cùng một bảng. Tùy chọn này cũng làm giảm tổng thời gian của quá trình chèn hàng loạt.

Khi chúng tôi thực hiện truy vấn sau trong quá trình chèn hàng loạt, chúng tôi có thể theo dõi chi tiết khóa và loại khóa:

SELECT dm_tran_locks.request_session_id,
       dm_tran_locks.resource_database_id,
       DB_NAME(dm_tran_locks.resource_database_id) AS dbname,
       CASE
           WHEN resource_type = 'OBJECT'
               THEN OBJECT_NAME(dm_tran_locks.resource_associated_entity_id)
           ELSE OBJECT_NAME(partitions.OBJECT_ID)
       END AS ObjectName,
       partitions.index_id,
       indexes.name AS index_name,
       dm_tran_locks.resource_type,
       dm_tran_locks.resource_description,
       dm_tran_locks.resource_associated_entity_id,
       dm_tran_locks.request_mode,
       dm_tran_locks.request_status
FROM sys.dm_tran_locks
LEFT JOIN sys.partitions ON partitions.hobt_id = dm_tran_locks.resource_associated_entity_id
LEFT JOIN sys.indexes ON indexes.OBJECT_ID = partitions.OBJECT_ID AND indexes.index_id = partitions.index_id
WHERE resource_associated_entity_id > 0
  AND resource_database_id = DB_ID()

Kết luận

Bài viết hiện tại đã khám phá tất cả các chi tiết về hoạt động chèn hàng loạt trong SQL Server. Đáng chú ý, chúng tôi đã đề cập đến lệnh BULK INSERT và các cài đặt và tùy chọn của nó. Ngoài ra, chúng tôi đã phân tích các tình huống khác nhau gần với các vấn đề trong cuộc sống thực.

Công cụ hữu ích:

dbForge Data Pump - phần bổ trợ SSMS để lấp đầy cơ sở dữ liệu SQL với dữ liệu nguồn bên ngoài và di chuyển dữ liệu giữa các hệ thống.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Xem lịch sử công việc của SQL Server Agent với SSMS

  2. Cách tốt nhất để kiểm tra kết nối SQL Server theo chương trình là gì?

  3. SQL Server Internals:Các toán tử có vấn đề Pt. III - Các loại

  4. Cách sử dụng các tính năng AlwaysOn của SQL Server

  5. Làm cách nào để giám sát các thay đổi của bảng SQL Server bằng cách sử dụng c #?