MVC 300K 行数据 table 从 excel 到 SQL 使用用户定义的 Table 类型 - 操作数类型冲突
MVC 300K row data table from excel into SQL using User-defined Table Types - Operand type clash
我想通过 MVC 和用户定义的 Table 类型将 300K + 行从 excel(热量监测数据)导入 MsSQL 数据库。
我选择使用 Table 类型和存储过程,因为我不想循环访问数据并创建动态 SQL 来上传 300K 条记录,因为这需要很长时间处理(试过了!)。
我使用的流程是:
- 使用 JSON
将数据从 MVC 页面导入控制器
- 将 InputStream 传递给 GetDataFromCSVFile 函数
- 创建数据Table 并填充流
- 使用数据类型和 table
创建新的 SqlParameter
- 然后我使用存储过程将数据 table 传递给 msSql 服务器。
完整代码。
Public Async Function DataImport(ByVal DataImports As HttpPostedFileBase) As Task(Of ActionResult)
If DataImports Is Nothing Then Return Json(New With {Key .Status = 0, Key .Message = "No File Selected"})
Try
Dim fileData = GetDataFromCSVFile(DataImports.InputStream)
Dim dtManualHeatData = fileData.ToArray()
Dim table As DataTable = New DataTable()
table.Columns.Add("ManualHeatDataId", GetType(String))
table.Columns.Add("DateAndTime", GetType(Date))
table.Columns.Add("Pulse", GetType(Integer))
For Each Line As Object In dtManualHeatData
Dim row As DataRow = table.NewRow()
row("ManualHeatDataId") = Line.ManualHeatDataId
row("DateAndTime") = Line.DateAndTime
row("Pulse") = Line.Pulse
table.Rows.Add(row)
Next
Dim tblManualHeatData = New SqlParameter("myManualHeatDatas", SqlDbType.Structured) With {.TypeName = "dbo.ManualHeatDatasType",
.Value = table
}
Dim SQL As String = "EXEC dbo.ImportManualHeatData @myManualHeatDatas"
Await db.Database.ExecuteSqlCommandAsync(SQL, tblManualHeatData)
Return Json(New With {Key .Status = 1, Key .Message = "File Imported Successfully "})
Catch ex As Exception
Return Json(New With {Key .Status = 0, Key .Message = ex.Message})
End Try
End Function
Private Function GetDataFromCSVFile(ByVal stream As Stream) As List(Of ManualHeatData)
Dim ManualHeatList = New List(Of ManualHeatData)()
Try
Using reader = ExcelReaderFactory.CreateReader(stream)
Dim dataset = reader.AsDataSet(New ExcelDataSetConfiguration() With {
.ConfigureDataTable = Function(__) New ExcelDataTableConfiguration() With {
.UseHeaderRow = True,
.ReadHeaderRow = Function(rowReader)
rowReader.Read()
rowReader.Read()
rowReader.Read()
rowReader.Read()
rowReader.Read()
rowReader.Read()
End Function}})
If dataset.Tables.Count > 0 Then
Dim dataTable = dataset.Tables(0)
Dim Count As Long = 0
For Each objDataRow As DataRow In dataTable.Rows
Count = +1
If objDataRow.ItemArray.All(Function(x) String.IsNullOrEmpty(x?.ToString())) Then Continue For
ManualHeatList.Add(New ManualHeatData() With {
.ManualHeatDataId = Count,
.DateAndTime = objDataRow("Date").ToString(),
.Pulse = objDataRow("Pulses (Pulses)").ToString()
})
Next
End If
End Using
Catch Ex As Exception
Throw
End Try
Return ManualHeatList
End Function
上面的整个过程直到 Await db.Database.ExecuteSqlCommandAsync 大约需要 15 秒,这对我的需要来说很好。问题是当 db.Database.ExecuteSqlCommandAsync 为 运行.
时出现以下错误
Operand type clash: ManualHeatDatasType is incompatible with nvarchar
我已经能够进入 SQL 服务器并查看由上述代码传递给它但未使用事件记录器删除的 SQL。当我查看此 SQL 语句时,它只有 3711 行长,当我使用 SSMS 激发它时,它会正确地将行添加到所需的 table 中。在事件记录器中看到但未执行的 SQL 语句示例如下。
declare @p3 dbo.ManualHeatDatasType
insert into @p3 values(N'1','2020-10-14 12:31:15',0)
insert into @p3 values(N'1','2020-10-14 12:31:25',1)
insert into @p3 values(N'1','2020-10-14 12:31:35',0)
..loads more rows!...
insert into @p3 values(N'1','2020-10-14 22:48:45',0)
insert into @p3 values(N'1','2020-10-14 22:48:55',0)
insert into @p3 values(N'1','2020-10-14 22:49:05',0)
insert into @p3 values(N'1','2020-10-14 22:49:15',0)
insert into @p3 values(N'1','2020-10-14 22:49:25',0)
insert into @p3 values(N'1','2020-10-14 22:49:35',0)
exec sp_executesql N'EXEC dbo.ImportManualHeatData @myManualHeatDatas',N'@myManualHeatDatas [dbo].[ManualHeatDatasType] READONLY',@myManualHeatDatas=@p3
如果我多次重复测试,它总是在传递给 SQL 而不是 运行 的 3711 行处停止。所以我在想这个问题与datatable/stream.
中3711或22:49:35之后的数据有关
但是这个点周围的所有数据都是干净的,并且与之前的数据完全相同。
如果有人对问题出在哪里有任何指示或反馈,请告诉我。
干杯,
理查德.
确定找到解决方案!
不使用存储过程,table 键入并将数据映射 table 到我找到的类型 SqlBulkCopy!!!
在上面的原始问题中创建 table 后,我可以使用如下所示的 SqlBulkCopy 将整个数据 table 推送到 SQL 服务器和整个过程从 excel 读取和导入 SQL 的 300k 行大约需要 5 秒!!令人兴奋!!
Using dbcontext As ApplicationDbContext = New ApplicationDbContext()
Dim sqlCon As SqlConnection = New SqlConnection(DbContext.Database.Connection.ConnectionString)
sqlCon.Open()
Using bulkCopy As SqlBulkCopy = New SqlBulkCopy(sqlCon)
bulkCopy.DestinationTableName = Data_Base + ".dbo.ManualHeatDatas"
Try
bulkCopy.WriteToServer(table)
Catch ex As Exception
Console.WriteLine(ex.Message)
Finally
table.Dispose()
End Try
End Using
End Using
我想通过 MVC 和用户定义的 Table 类型将 300K + 行从 excel(热量监测数据)导入 MsSQL 数据库。
我选择使用 Table 类型和存储过程,因为我不想循环访问数据并创建动态 SQL 来上传 300K 条记录,因为这需要很长时间处理(试过了!)。
我使用的流程是:
- 使用 JSON 将数据从 MVC 页面导入控制器
- 将 InputStream 传递给 GetDataFromCSVFile 函数
- 创建数据Table 并填充流
- 使用数据类型和 table 创建新的 SqlParameter
- 然后我使用存储过程将数据 table 传递给 msSql 服务器。
完整代码。
Public Async Function DataImport(ByVal DataImports As HttpPostedFileBase) As Task(Of ActionResult)
If DataImports Is Nothing Then Return Json(New With {Key .Status = 0, Key .Message = "No File Selected"})
Try
Dim fileData = GetDataFromCSVFile(DataImports.InputStream)
Dim dtManualHeatData = fileData.ToArray()
Dim table As DataTable = New DataTable()
table.Columns.Add("ManualHeatDataId", GetType(String))
table.Columns.Add("DateAndTime", GetType(Date))
table.Columns.Add("Pulse", GetType(Integer))
For Each Line As Object In dtManualHeatData
Dim row As DataRow = table.NewRow()
row("ManualHeatDataId") = Line.ManualHeatDataId
row("DateAndTime") = Line.DateAndTime
row("Pulse") = Line.Pulse
table.Rows.Add(row)
Next
Dim tblManualHeatData = New SqlParameter("myManualHeatDatas", SqlDbType.Structured) With {.TypeName = "dbo.ManualHeatDatasType",
.Value = table
}
Dim SQL As String = "EXEC dbo.ImportManualHeatData @myManualHeatDatas"
Await db.Database.ExecuteSqlCommandAsync(SQL, tblManualHeatData)
Return Json(New With {Key .Status = 1, Key .Message = "File Imported Successfully "})
Catch ex As Exception
Return Json(New With {Key .Status = 0, Key .Message = ex.Message})
End Try
End Function
Private Function GetDataFromCSVFile(ByVal stream As Stream) As List(Of ManualHeatData)
Dim ManualHeatList = New List(Of ManualHeatData)()
Try
Using reader = ExcelReaderFactory.CreateReader(stream)
Dim dataset = reader.AsDataSet(New ExcelDataSetConfiguration() With {
.ConfigureDataTable = Function(__) New ExcelDataTableConfiguration() With {
.UseHeaderRow = True,
.ReadHeaderRow = Function(rowReader)
rowReader.Read()
rowReader.Read()
rowReader.Read()
rowReader.Read()
rowReader.Read()
rowReader.Read()
End Function}})
If dataset.Tables.Count > 0 Then
Dim dataTable = dataset.Tables(0)
Dim Count As Long = 0
For Each objDataRow As DataRow In dataTable.Rows
Count = +1
If objDataRow.ItemArray.All(Function(x) String.IsNullOrEmpty(x?.ToString())) Then Continue For
ManualHeatList.Add(New ManualHeatData() With {
.ManualHeatDataId = Count,
.DateAndTime = objDataRow("Date").ToString(),
.Pulse = objDataRow("Pulses (Pulses)").ToString()
})
Next
End If
End Using
Catch Ex As Exception
Throw
End Try
Return ManualHeatList
End Function
上面的整个过程直到 Await db.Database.ExecuteSqlCommandAsync 大约需要 15 秒,这对我的需要来说很好。问题是当 db.Database.ExecuteSqlCommandAsync 为 运行.
时出现以下错误Operand type clash: ManualHeatDatasType is incompatible with nvarchar
我已经能够进入 SQL 服务器并查看由上述代码传递给它但未使用事件记录器删除的 SQL。当我查看此 SQL 语句时,它只有 3711 行长,当我使用 SSMS 激发它时,它会正确地将行添加到所需的 table 中。在事件记录器中看到但未执行的 SQL 语句示例如下。
declare @p3 dbo.ManualHeatDatasType
insert into @p3 values(N'1','2020-10-14 12:31:15',0)
insert into @p3 values(N'1','2020-10-14 12:31:25',1)
insert into @p3 values(N'1','2020-10-14 12:31:35',0)
..loads more rows!...
insert into @p3 values(N'1','2020-10-14 22:48:45',0)
insert into @p3 values(N'1','2020-10-14 22:48:55',0)
insert into @p3 values(N'1','2020-10-14 22:49:05',0)
insert into @p3 values(N'1','2020-10-14 22:49:15',0)
insert into @p3 values(N'1','2020-10-14 22:49:25',0)
insert into @p3 values(N'1','2020-10-14 22:49:35',0)
exec sp_executesql N'EXEC dbo.ImportManualHeatData @myManualHeatDatas',N'@myManualHeatDatas [dbo].[ManualHeatDatasType] READONLY',@myManualHeatDatas=@p3
如果我多次重复测试,它总是在传递给 SQL 而不是 运行 的 3711 行处停止。所以我在想这个问题与datatable/stream.
中3711或22:49:35之后的数据有关但是这个点周围的所有数据都是干净的,并且与之前的数据完全相同。
如果有人对问题出在哪里有任何指示或反馈,请告诉我。 干杯, 理查德.
确定找到解决方案!
不使用存储过程,table 键入并将数据映射 table 到我找到的类型 SqlBulkCopy!!!
在上面的原始问题中创建 table 后,我可以使用如下所示的 SqlBulkCopy 将整个数据 table 推送到 SQL 服务器和整个过程从 excel 读取和导入 SQL 的 300k 行大约需要 5 秒!!令人兴奋!!
Using dbcontext As ApplicationDbContext = New ApplicationDbContext()
Dim sqlCon As SqlConnection = New SqlConnection(DbContext.Database.Connection.ConnectionString)
sqlCon.Open()
Using bulkCopy As SqlBulkCopy = New SqlBulkCopy(sqlCon)
bulkCopy.DestinationTableName = Data_Base + ".dbo.ManualHeatDatas"
Try
bulkCopy.WriteToServer(table)
Catch ex As Exception
Console.WriteLine(ex.Message)
Finally
table.Dispose()
End Try
End Using
End Using