0

我目前正在尝试将数据输入 SQL 的不同方法,昨天使用 BCP 遇到了一个问题,尽管我解决了这个问题,但由于没有非常有用的错误信息,这让我想起了使用 SSIS 包。我觉得对于我喜欢的工作方式,我会更乐意将整个数据行加载到临时表中(无论是固定宽度还是定界)(使用 BCP 或批量插入),然后对数据行进行操作,而不是试图强制它们输入正在进入 SQL 的列。

因此,我想找到一种方法,允许我在将数据插入其目标之前拆分和验证(检查数据类型)数据,并将任何错误的数据行写入另一个表,以便我可以决定如何处理它们.

我已经拼凑了一个脚本来模拟场景,importedData 表将是我的 BCP 或 BULK INSERT 的输出。ImportedData 中的所有数据都需要在 Presenters 或 RejectedData 表中结束。

我需要一种可以合理扩展的方法,现实生活中的情况可能更像是 40 列和 2000 万行数据,所以我想我必须一次处理 10,000 行。

SQL Server 2012 具有新的 try_parse 函数,这可能会有所帮助,但我需要能够在 2005 和 2008 机器上执行此操作。

IF OBJECT_ID (N'ImportedData', N'U') IS NOT NULL DROP TABLE dbo.ImportedData
CREATE TABLE dbo.ImportedData (RowID INT IDENTITY(1,1), DataRow VARCHAR(MAX))

IF OBJECT_ID (N'Presenters', N'U') IS NOT NULL DROP TABLE dbo.Presenters
CREATE TABLE dbo.Presenters (PresenterID INT, FirstName VARCHAR(10), LastName VARCHAR(10))

IF OBJECT_ID (N'RejectedData', N'U') IS NOT NULL DROP TABLE dbo.RejectedData
CREATE TABLE dbo.RejectedData (DataRow VARCHAR(MAX))

-- insert as fixed-width
INSERT INTO dbo.ImportedData(DataRow)
SELECT           '1  Bruce     Forsythe  '                               
UNION ALL SELECT '2  David     Dickinson '
UNION ALL SELECT 'X  BAD       DATA'                                  
UNION ALL SELECT '3  Keith     Chegwin   '                        

-- insert as CSV
/*INSERT INTO dbo.ImportedData(DataRow)
SELECT '1,Bruce,Forsythe'                               
UNION ALL SELECT '2,David,Dickinson'                                  
UNION ALL SELECT 'X,BAD,DATA'
UNION ALL SELECT '3,Keith,Chegwin' 
*/
---------- DATA PROCESSING -------------------------------

SELECT
    SUBSTRING(DataRow,1,3) AS ID,
    SUBSTRING(DataRow,4,10) AS FirstName,
    SUBSTRING(DataRow,14,10) AS LastName
FROM
    ImportedData


---------- DATA PROCESSING -------------------------------
SELECT * FROM ImportedData
SELECT * FROM Presenters
SELECT * FROM RejectedData
4

2 回答 2

1

对于您的 20M 行场景和对性能的担忧,让我们深入研究一下。

第一步,将大文件加载到数据库中。文件系统将写入磁盘并读取所有数据。也许您正坐在一堆 Fusion-io 驱动器上,而 iops 不是问题,但考虑到这种不太可能发生的情况,您将花费 X 时间通过 bcp/bulk insert/ssis/.net/etc 从磁盘读取数据。然后,您将花时间以表格插入的形式将所有相同的数据写回磁盘。

第 2 步,解析该数据。在我们花费任何 CPU 时间运行这些子字符串操作之前,我们需要识别数据行。如果您的机器在 RAM 上配置良好,那么 ImportedData 的数据页可能在内存中,访问它们的成本要低得多。奇怪的是,它们并不都在内存中,因此会发生逻辑和物理读取的组合来获取该数据。您现在已经有效地读取了该源文件两次,但没有任何收获。

现在是时候开始拆分数据了。开箱即用,TSQL 将为您提供 trims、left、right 和 substring 方法。使用 CLR,您可以获得一些 .NET 字符串库的包装方法,以帮助简化编码工作,但您将用实例化成本换取一些编码效率。最后我读到这个问题的答案是(tsql vs clr)是“这取决于”。如果您了解社区,这会令人震惊,但这实际上取决于您的字符串长度和许多因素。

最后,我们准备解析这些值并查看它是否是合法值。正如您所说,对于 SQL 2012,我们有try_parsetry_convert。Parse 是全新的,但如果您需要处理语言环境感知数据(01-02-05 在 GB 中,是 2005 年 2 月 1 日。在美国,是 2005 年 1 月 2 日。在 JP,是 2001 年 2 月 5 日),这是非常宝贵的。如果您不在 2012 年,则可以使用 CLR 包装器推出自己的版本。

第 3 步,错误!有人在一个糟糕的约会或其他什么事情上滑倒了,你的演员阵容失败了。发生什么了?由于查询要么成功,要么不成功,所有的行都会失败,并且您会收到非常有用的错误消息,例如“字符串或二进制数据将被截断”或“从字符串转换日期时间时转换失败”。您的 N 大小切片中的哪一行?直到你去寻找它,你才会知道,这就是人们通常转向进一步降低性能的 RBAR 方法的时候。或者他们尝试保持基于集合,但针对在尝试插入之前转换失败的场景对源集过滤运行重复查询。

你可以从我的标签中看出,我是一个 SSIS 类型的人,但我并不偏执地认为这是唯一可以工作的东西。如果有 ETL 的方法,我想我已经尝试过了。在您的情况下,我认为您将通过开发自己的 ETL 代码/框架或使用现有的(犀牛反应式)获得更好的性能和可扩展性

最后,注意 varchar(max) 的含义。它有与之相关的性能成本。

此外,如上所述,您的流程将只允许 ETL 的单个实例同时运行。也许这涵盖了您的用例,但是在我们进行大量 ETL 的公司中,我们不能强迫客户 B 在开始工作之前等待客户 A 的 etl 完成处理,否则我们将立即缺少客户。

于 2012-08-24T19:17:11.460 回答
0

在 T-SQL 中没有简单的方法。在这种情况下,对于您将尝试解析的所有数据类型,您需要具有isdate() 、isnumeric() 类型的 UDF 。然后您可以将被拒绝的行移到被拒绝的表中,从importeddate中删除这些行,然后继续加载..

SELECT 
RecordID,
SUBSTRING(DataRow,1,3) AS ID, 
SUBSTRING(DataRow,4,10) AS FirstName, 
SUBSTRING(DataRow,14,10) AS LastName,
SUBSTRING(DataRow,24,8) AS DOB,
SUBSTRING(DataRow,32,10) AS Amount,     
INTO RejectedData 
FROM ImportedData
WHERE  ISDATE(SUBSTRING(DataRow,24,8))= 0 
OR ISNUMERIC(SUBSTRING(DataRow,32,10))=0

然后从导入的数据中删除

DELETE FROM ImportedData WHERE RecordID IN (SELECT RecordID FROM RejectedData )

然后插入演示者

INSERT INTO Presenters     
SELECT 
RecordID,
SUBSTRING(DataRow,1,3) AS ID, 
SUBSTRING(DataRow,4,10) AS FirstName, 
SUBSTRING(DataRow,14,10) AS LastName,
CONVERT(Date,SUBSTRING(DataRow,24,8)) AS DOB,
CONVERT(DECIMAL(18,2),SUBSTRING(DataRow,32,10)) AS Amount,  
FROM ImportedData

对于在插入中管理批次,这是一篇非常好的文章。

http://sqlserverplanet.com/data-warehouse/transferring-large-amounts-of-data-using-batch-inserts

于 2012-08-27T19:34:44.863 回答