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 条记录,因为这需要很长时间处理(试过了!)。

我使用的流程是:

完整代码。

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