Ray*_*ond 7 sql-server optimization bulk sql-server-2008-r2 import
我目前正在开发一个项目,该项目从平面文件 (csv) 中批量导入数据,大约 18 个不同的文件通过一些存储过程链接到特定的表。
我按照数据加载性能指南中的建议进行了操作。
数据库处于BulkLogged
恢复模式以最小化日志记录,当在包含 600000 行的文件上执行以下存储过程时,我收到错误
消息 9002,级别 17,状态 4,过程 SP_Import__DeclarationClearanceHistory_FromCSV,第 34 行
数据库的事务日志已满。要找出无法重用日志中的空间的原因,请参阅 sys.databases 中的 log_reuse_wait_desc 列
(出于测试目的,我在开始导入之前进行了完整备份)。
看着log_reuse_wait_desc
我看到以下内容:
log_reuse_wait_desc
检查点。所有其他导入成功导入。
欢迎任何解决此问题的意见。
PROCEDURE [dbo].[SP_Import_DeclarationClearanceHistory_FromCSV]
@FilePath [nvarchar](1000)
AS
BEGIN
-- Creating a Temproary Table for importing the data from csv file.
DBCC TRACEON(610)
CREATE TABLE #DeclarationClearanceHistory
(
[ItemID] [int] IDENTITY(1, 1) NOT NULL ,
[CMSDeclarationID] [bigint] NOT NULL ,
[StatusCode] [nvarchar](10) NOT NULL ,
[SubStatus] [nvarchar](10) NULL ,
[DepartmentCode] [nvarchar](10) NULL ,
[StartDate] [datetime] NULL ,
[EndDate] [datetime] NULL ,
PRIMARY KEY CLUSTERED ( [ItemID] ASC )
WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]
)
ON [PRIMARY]
-- Inserting all the from csv to temproary table using BULK INSERT
EXEC ('BULK INSERT #DeclarationClearanceHistory
FROM ''' + @FilePath + '''
WITH ( FIELDTERMINATOR = ''<,>'', ROWTERMINATOR =''\n'', FIRSTROW = 2, KEEPIDENTITY, CODEPAGE = ''ACP'', ORDER = ''ITEMID ASC'' );') ;
-- By using MERGE statement, inserting the record if not present and updating if exist.
MERGE dbo.DeclarationClearanceHistory AS TargetTable -- Inserting or Updating the table.
USING #DeclarationClearanceHistory AS SourceTable -- Records from the temproary table (records from csv file).
ON ( TargetTable.ItemID = SourceTable.ItemID ) -- Defining condition to decide which records are alredy present
WHEN NOT MATCHED BY TARGET
THEN INSERT (
ItemID ,
CMSDeclarationID ,
StatusCode ,
SubStatus ,
DepartmentCode ,
StartDate ,
EndDate
)
VALUES ( SourceTable.ItemID ,
SourceTable.CMSDeclarationID ,
SourceTable.StatusCode ,
SourceTable.SubStatus ,
SourceTable.DepartmentCode ,
SourceTable.StartDate ,
SourceTable.EndDate
)
WHEN MATCHED -- If matched then UPDATE
THEN UPDATE
SET TargetTable.ItemID = SourceTable.ItemID ,
TargetTable.CMSDeclarationID = SourceTable.CMSDeclarationID ,
TargetTable.StatusCode = SourceTable.StatusCode ,
TargetTable.SubStatus = SourceTable.SubStatus ,
TargetTable.DepartmentCode = SourceTable.DepartmentCode ,
TargetTable.StartDate = SourceTable.StartDate ,
TargetTable.EndDate = SourceTable.EndDate ;
DBCC TRACEOFF(610)
END
Run Code Online (Sandbox Code Playgroud)
我的第一条评论是,您正在执行 ELT(提取、加载、转换)而不是 ETL(提取、转换、加载)。虽然 ELT 利用基于集合的关系优势并且速度非常快,但有时它们的写入密集度很高(存储困难)。具体来说,t 日志。这是因为转换是在磁盘上完成的(通常是更新或插入)。如果可能的话,我更喜欢 ETL,因为转换是在缓冲区中完成的,并且如果正确完成,则需要最少的 t-log 写入。缓冲液很便宜。快速存储则不然。对于某些批量操作,t-log 是一个非增值瓶颈。
以下是您正在做的一些事情,但我不推荐。
看来您已经很好地涵盖了最低限度的日志记录规则。您正在使用 tf 610(指定的排序键)在批量记录模式下加载到没有非集群的空 B 树。在临时表之外,这里一切看起来都很好。只要文件实际上是按键排序的,就应该没问题。您是在 tempdb 还是用户数据库上弹出日志?
关于合并语句:
更新将始终被完整记录。您是否正在更改表格的很大一部分?如果是这样,您可能会考虑在内存中进行合并(SSIS 数据流任务或.Net),然后批量加载到新表中。这是更多的工作,但大部分工作是在缓冲区中完成的,并且使用了最少的 t-log。如果更改部分很大,则最少记录的插入可能比完全记录的更新更快。
由于您使用的是 tf 610,因此在使用 tabblock 提示时插入可以最少地记录日志。有关与 tabblock 合并的更多信息,请参阅此处:链接注意,如果您采用此路线,更新仍将被完整记录。