我正在查看我编写的旧网络应用程序,它需要大约一个小时才能从DataTable
读取4500条记录,因此可以将它们写入CSV文件。我觉得必须有一些方法来改善这一点。
很少有事情需要注意:
DataTable包含... 376列
至少,我认为Excel的NL
列转换为。我现在只是查看了列数,并且不知道有那么多。我们的软件供应商还没有意识到这个过程的动态sql语句的价值,所以每个软件“升级”只是不断添加更多列而不是仅选择所需的列。
我无法更改生成数据的SQL语句
根据数据类型,数据需要以特定格式进行格式化
数据确实包含特殊字符,例如逗号
缓慢的部分是读取数据。从SQL服务器获取数据并将其写入CSV很快。
这是代码。原谅这个烂摊子,当我不知道我在做什么以及什么时候我还在VB工作时我把它写回来
Function ReadDataTableForCSV(dt as DataTable)
Dim sb = New StringBuilder()
Dim dataTypes As New Dictionary(Of String, Integer)
' Header Row
For i as Integer = 0 to dt.Columns.Count - 1
Dim col as DataColumn = dt.Columns(i)
Dim t = col.DataType
If t is GetType(Boolean) Then
dataTypes.Add(i, 1)
Else If t is GetType(Decimal) Then
dataTypes.Add(i, 2)
Else
dataTypes.Add(i, 3)
End If
sb.Append(String.Format("""{0}""", col.ColumnName))
sb.Append(Iif(i = dt.Columns.Count - 1, vbLf, ","))
Next
' Items
For Each row as DataRow in dt.Rows
For i As Integer = 0 To dt.Columns.Count - 1
Select dataTypes(i)
Case 1
sb.Append(String.Format("""{0}""", CInt(row(i))))
Case 2
sb.Append(String.Format("""{0}""", FormatNumber(row(i), 2, , , 0)))
Case 3
sb.Append(String.Format("""{0}""", row(i)))
End Select
sb.Append(Iif(i = dt.Columns.Count - 1, vbLf, ","))
Next
Next
End Function
修改:删除与问题无关的代码
答案 0 :(得分:2)
以下是我将如何重写它:
预先分配stringbuilder内存。
将数据类型从字典更改为字节数组,仅使用值1和2;值3现在为0,这将是数组中项目的默认值。
使用列中的Ordinal属性而不是单独的索引。
简化项目和行分隔符循环内的评估。
使用Decimal.ToString而不是FormatNumber。
删除iifs(这些可能是由编译器优化的,但我从VB早期开始仍然对它们持怀疑态度)
以下是代码:
Function ReadDataTableForCSV(dt As DataTable)
Dim sb As New StringBuilder(100000000)
Dim dataTypes As Byte()
ReDim dataTypes(dt.Columns.Count - 1)
' Header Row
For Each col As DataColumn In dt.Columns
If sb.Length <> 0 Then
sb.Append(",")
End If
Select Case col.DataType.ToString
Case "System.Boolean"
dataTypes(col.Ordinal) = 1
Case "System.Decimal"
dataTypes(col.Ordinal) = 2
' Everything else defaults to 0
End Select
sb.Append("""").Append(col.ColumnName).Append("""")
Next
sb.AppendLine()
' or sb.Append(vbLf)
' Items
For Each row As DataRow In dt.Rows
For i As Integer = 0 To dt.Columns.Count - 1
If i <> 0 Then
sb.Append(",")
End If
sb.Append("""")
Select Case dataTypes(i)
Case 1
If CBool(row(i)) Then
sb.Append("1")
Else
sb.Append("0")
End If
Case 2
sb.Append(CDec(row(i)).ToString("F"))
Case Else
sb.Append(row(i))
End Select
sb.Append("""")
Next
sb.AppendLine()
' or sb.Append(vbLf)
Next
End Function
答案 1 :(得分:0)
结帐Filehelpers。它非常快,处理引用的字段,并简化了读取和写入分隔和固定宽度文件。
可以在Codeproject上找到演练。
如果您使用类似Dapper的ORM将数据库中的记录读入强类型类,那么您可以将ORM中的强类型类数组(将使用DelimitedRecord和FieldConverter属性修饰)传递给Filehelpers Engine。
Sourceforge和Filehelpers网站上的下载链接有点旧。它有效,但我建议从Sourceforge中提取最新资源。
修改强>
没有什么能成为单一的表现。也许这是一个小点击的集合?试一试。
这是黑暗重写的镜头。 :)
Function ReadDataTableForCSV(dr As SqlDataReader) As String
Dim fieldCount As Integer = dr.FieldCount
Dim sb = New StringBuilder()
' Header Row
For i As Integer = 0 To fieldCount - 1
sb.AppendFormat("""{0}""", dr.GetName(i))
sb.Append(If(i = fieldCount - 1, vbLf, ","))
Next
' Items
While dr.Read()
For i As Integer = 0 To fieldCount - 1
Dim t As Type = dr.GetFieldType(i)
sb.Append("""") 'quoted cell
If t Is GetType(Boolean) Then
sb.Append(If(dr.GetBoolean(i), "1", "0"))
ElseIf t Is GetType(Decimal) Then
sb.Append(dr.GetDecimal(i).ToString("#.##"))
Else
sb.Append(dr(i))
End If
sb.Append("""") 'quoted cell
sb.Append(If(i = fieldCount - 1, vbLf, ","))
Next
End While
Return sb.ToString()
End Function
答案 2 :(得分:0)
如果获取数据表是瓶颈,并且您无法更改sql语句并且结果以数据表形式返回,则无法完成任何操作。
如果你可以控制结果的返回方式,那么返回一个不可数的。其中T是POCO或IDataRecord,使用延迟执行。这将使内存消耗保持在较低水平,因为您只会在使用时加载记录。
读取数据可能如下所示
while(reader.Reader())
{
yield return reader; // or reader.ConvertRecordToObject<T>(); //extension method
}
然后你可以foreach
将结果写到csvfile
foreach(var record in GetEnumerationOfRecords())
{
WriteToCsv(record);
}
这仍然留下了sql语句的问题,这很可能导致大部分性能问题,但你无法做任何事情。