【问题标题】:Preserve row order in bulk insert在批量插入中保留行顺序
【发布时间】:2017-08-23 13:32:58
【问题描述】:

我正在尝试从文本文件中批量插入表格并保留行顺序。我这样做有两个原因:

  • 我需要检查第一行是否是标题,如果是则删除它
  • 我正在报告“错误”(列值应该是什么与给出的值之间的差异,即字母数字字段中的一个字母),并且需要告诉他们它们发生在文本文件中的哪一行。同时我仍然需要插入这些行,只是在有错误的列上使用空白值。

要进行批量插入,我首先将其插入到临时表中,然后将清理后的值插入到主表中。但是临时表似乎是随机顺序的。

输入文件没有任何特定的顺序,所以我无法通过 order-by 查询来解决这个问题。

对我可以在这里做什么有什么建议吗?

【问题讨论】:

  • 您使用了错误的工具。 SSIS 就是为此目的而设计的,但它是一个复杂的工具。并且您将报告“错误”的想法毫无意义 - 除非您计划插入字符串列表然后检查数据类型问题 RBAR。在 tsql 中执行此操作将极具挑战性。
  • @SM 或者这是在 WPF 应用程序中。它需要离线运行并使用本地数据库。我报告“错误”确实是有道理的,因为我们的审计员想知道是否有空白字段,或者应该有数字的字母而不梳理数十万条记录。我已经使用您描述的方法成功地做到了这一点,只是没有报告正确的行号。该应用程序运行速度非常快,并且除了行号问题之外运行良好。

标签: sql-server wpf vb.net tsql


【解决方案1】:

对于您要插入的表或临时表,只需有一列带有 IDENTITY PROPERTY

create table #temp (ID int identity(1,1), otherColumn varchar(16), ...)
--do bulk insert

但是,对您来说更理想的是在源文件中创建一个唯一的行号。

【讨论】:

  • 我似乎无法向临时表添加标识,因为这些列应该与文本文档匹配。我收到类型不匹配错误。另外,如果可能的话,标识是否与文本文件的行号匹配?
  • 看起来您不能将它与批量插入一起使用。因此,我只需在源数据中创建唯一的行号。如果是excel,这很容易。或者....您可以批量插入临时文件。换一个有身份的临时工,做你想做的事。
  • 第二个临时表的标识与文本文件中的行不匹配。向源数据添加行号不是可行的解决方案。
  • 这里的响应很晚,但我最终确实通过将源文件逐行复制到单独的文本文件中来在源文件中添加行号。
【解决方案2】:

创建一个带有标识列的表,然后批量插入到该表顶部的视图中,如下所示:

CREATE TABLE tb_TextFile (ID int identity(1,1), someColumn varchar(16))
GO

CREATE VIEW vw_TextFile
AS
SELECT someColumn
FROM tb_TextFile
GO

BULK INSERT vw_TextFile
FROM ...

【讨论】:

  • 您可能需要将 ID 添加为 INT IDENTITY(1,1) PRIMARY KEY 以使表具有聚集索引。 (至少从上面的示例来看,这是一种创建聚集索引的简单方法。)添加索引并添加 TABLOCK 提示并使用视图似乎是一个可行的选择。
  • FROM docs.microsoft.com/en-us/previous-versions/sql/…: "在 SQL Server 2005 和更高版本中,在具有聚集索引的表上指定 TABLOCK 会阻止并行批量导入数据。如果您想在这种情况下执行并行批量导入, 不要使用 TABLOCK。有关并行批量加载的详细信息,请参阅优化批量导入指南。”
  • 今晚我成功地使用了这个想法来修复文本文件的无序导入。顺序很重要,文本文件中没有可用的密钥。我将 PRIMARY KEY 添加到 ID 列以确保它是 CLUSTERED INDEX 并且还在仅选择 NVARCHAR(MAX) 列的 VIEW 中添加了插入时的 WITH (TABLOCK) 提示。该视图是一个很好的提示,但我认为 PK 和 TABLOCK 提示可能也是保证顺序保持所必需的。
【解决方案3】:
 Using _con As New SqlConnection(ConnectionString)
  _con.Open()
  If Not (String.IsNullOrEmpty(Pkey)) Then
    deleteTbl = "Delete from " & destTableName & " WHERE " & Pkey & " = " & PkeyId
    Using cmdTable As New SqlCommand(deleteTbl, _con)
      cmdTable.ExecuteNonQuery()
    End Using
  End If


  Using bulkCopy As SqlBulkCopy = New SqlBulkCopy(_con)
    bulkCopy.BulkCopyTimeout = 0
    bulkCopy.BatchSize = 50000
    bulkCopy.DestinationTableName = destTableName 'it is a tablename
    bulkCopy.WriteToServer(sourceTable)  'sourceTable is a datatable
  End Using

End Using

【讨论】:

    【解决方案4】:

    从 Christophej 的建议中添加一些其他内容:

    CREATE TABLE dbo.twrkImportFileLine ( ID INT IDENTITY(1,1) PRIMARY KEY, LineData NVARCHAR(MAX) ); 
    
    GO
    
    CREATE OR ALTER VIEW dbo.vw_twrkImportFileLine AS
    SELECT LineData
    FROM twrkImportFileLine;
    
    GO
    
    --INSERT INTO vw_twrkImportFileLine (LineData) VALUES ('Test inserting text into the table through the view'); 
    --SELECT * FROM vw_twrkImportFileLine
    --DELETE FROM vw_twrkImportFileLine
    
    BULK INSERT dbo.vw_twrkImportFileLine FROM '\\SERVERNAME\Import Folder\FileName.txt' WITH (TABLOCK) ;
    
    SELECT LineData
    FROM dbo.twrkImportFileLine 
    ORDER BY ID;
    

    在我测试的 815 行示例文件中维护了该顺序。以前如果没有这种方法,很可能是由于并行性,在大部分情况下都会出现故障。

    【讨论】:

      猜你喜欢
      • 2011-09-20
      • 2012-03-24
      • 2018-12-10
      • 1970-01-01
      • 2010-10-14
      • 2012-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多