ASP.NET Web 表单 - SQL 到 CSV 输出极慢
ASP.NET Web Form - SQL to CSV Output extremely slow
我使用 this 指南在 VS 2019 中构建了一个 ASP.NET Web 表单,带有一个按钮,该按钮在我的 SQL 服务器上执行存储过程,并将结果写入 CSV通过浏览器下载文件。
一切都很好,除了它非常慢。
例如,它花了一个多小时才生成一个 3.4 MB 的 CSV,其中包含 20K 行。
有什么办法可以加快速度吗?
这是我的代码,因为我确实根据搜索此问题时找到的内容稍作修改,但 none 不幸的是,它有所帮助:
Protected Sub ExportCSV(ByVal sender As Object, ByVal e As EventArgs)
Dim sqlcmd As String = "EXEC my_sp_name;"
Dim constr As String = ConfigurationManager.ConnectionStrings("CONNSTR").ConnectionString
Using con As New SqlConnection(constr)
Using cmd As New SqlCommand(sqlcmd)
Using sda As New SqlDataAdapter()
cmd.Connection = con
sda.SelectCommand = cmd
Using dt As New DataTable()
sda.Fill(dt)
Dim csv As String = String.Empty
For Each column As DataColumn In dt.Columns
csv += column.ColumnName + ","
Next
csv += vbCrLf
For Each row As DataRow In dt.Rows
For Each column As DataColumn In dt.Columns
csv += row(column.ColumnName).ToString().Replace(",", ";") + ","
Next
csv += vbCrLf
Next
Response.Clear()
Response.ClearHeaders()
Response.ClearContent()
Response.Buffer = True
Response.AddHeader("content-disposition", "attachment;filename=Data.csv")
Response.Charset = Encoding.UTF8.WebName
Response.ContentType = "text/csv"
Response.Output.Write(csv)
Response.Flush()
Response.End()
End Using
End Using
End Using
End Using
End Sub
我的 SQL 服务器上没有 activity,而它是 运行。我什至可以在我的 SQL 服务器上用相同的参数在几秒钟内手动执行相同的存储过程。
我不知道该怎么办。因此,非常感谢任何帮助。
好的,有一个简单的解释来解释为什么 运行 这么慢。事实上,您可以使 运行 快几百万倍!
首先:
数据库和索引? 20,000 行不是问题。事实上,如果您使用在垃圾箱中找到的旧计算机?它将很容易每秒拉取 100,000 行 - 并且在没有任何索引的情况下对数据库执行此操作!! - 即使您应用排序和标准。
所以,不,这不是数据库问题 - 您的速度非常快,实际上您需要一些努力才能使数据库 运行 变慢。
那么,那到底是什么问题呢?
为什么当然是字符串拼接!!!!
这样想:
假设你要走 150 英里
但是,说什么时候达到 100 英里?去第 101 英里?
嗯,你回到1英里,然后走100英里+1英里。
然后是 102 英里?
您回到第 1 英里,然后步行 102 英里
然后是 103 英里?
您回到第 1 英里,然后步行 103 英里
请注意,仅仅 100 到 103,您现在已经走了 300 多英里!!!
您的字符串连接出现相同情况。事实上,在您的字符串达到大约 2000 个左右的字符后,您会看到速度大幅下降。
那么,假设您的字符串现在有 500,000 个字符?
然后你去
MyString = MyString & "Hello"
它有什么作用?
为什么它从字符位置 1 开始,经过 500,000 个字符,然后添加第二部分。
所以,这就像走路的问题一样!!!
顺便说一句,最有趣的是有些人建议检查数据库速度。
事实上,如果你去 Microsoft 或 Google?
这样的地方面试
他们会问一大堆问题,以您作为开发人员为中心,回答有关多大、多远、多少的问题。
换句话说,他们正在寻找具有规模感的开发人员。
我的意思是,您可以步行到 1 个街区外的商店。但是 10 英里以外呢?不,你只能在一天内实际做到这一点! (到商店 10 英里,返回 10 英里)。因此,您需要使用某种交通工具。 (而且您需要意识到您不能再步行去商店了 - 可以吗??)。
可爱的小问题 post 这里是一个完美的例子,就是需要这种简单的思考来解释为什么 运行 这么慢!
事实上,删除所有数据库和所有文件操作。
试试这个简单的循环:
Dim str1 As String = ""
Dim str2 As String = Space(500)
Dim t As Long = Date.Now.Ticks
Dim i As Integer
For i = 1 To 3000
str1 = str1 + str2
Next
Dim td As Double = (Date.Now.Ticks - t) / 10000 / 1000
TextBox1.Text = td
那么,到 3000 的简单循环?
(您的计算机可以 for/next 在不到 1 秒的时间内循环到 10 亿!!!)
大约需要 10 秒(在较慢的计算机上)。
但是,现在让我们尝试 6000。(但请记住,您每次都回到起点,并且每次都走得更远才能到达终点)。它不是线性增长过程 - 而是指数增长!!!!
那么,需要 20 秒吗?
不,因为每次,你都会回到起点,遍历所有的终点,然后添加第二个字符串。而且每走一步都走得更远(更长)!!!
那么,6000?应该需要 20 秒,对吗?
不!!!
我们得到:48 秒!!! -- 时间增长呈指数增长!!!1
现在你知道为什么那些招聘公司会问上面那种求职面试问题了吧!!! - 多高,多远,多大!!!您必须能够根据这些类型的问题进行思考。
那么,如果我们创建新的数据行,但将其“添加”为一个数组,甚至是一个列表(这样就很容易将其作为行写到文件中),会发生什么情况。
现在让我们试试这个:
Dim str2 As String = Space(500)
Dim myList As List(Of String) = New List(Of String)
Dim t As Long = Date.Now.Ticks
Dim i As Integer
For i = 1 To 6000
myList.Add(str2)
Next
Dim td As Double = (Date.Now.Ticks - t) / 10000 / 1000
TextBox1.Text = td
时间:0
它运行速度如此之快,以至于它甚至都没有注册。
让我们转到 100,000 行。
例如:
For i = 1 To 100000
myList.Add(str2)
Next
时间:0.005033
5 1/1000 秒!
请注意这是多么惊人的速度!!!! - 它不仅是 1000 倍,而且速度快了 100 倍 运行!!!!
那么,对于您的代码?好吧,既然上面的速度非常快,那么我们可以在这里偷懒一点,甚至不必创建一行并将其写出来。因为我们正在处理 20,000 行?这么小,又是为了节省时间和简化代码?
我们可以用上面的。但是,如果这是 50k 或 100k 行呢?那么是的,我建议主循环创建行,然后将其发送到文件。
但是,我们可以这样说:(空气代码如下!!!)
Dim MyOutList As List(Of String) = New List(Of String)
Dim csv As String = ""
For Each column As DataColumn In dt.Columns
csv += column.ColumnName + ","
Next
MyOutList.Add(csv)
For Each row As DataRow In dt.Rows
csv = ""
For Each column As DataColumn In dt.Columns
csv += row(column.ColumnName).ToString().Replace(",", ";") + ","
Next
MyOutList.Add(csv)
Next
Response.Clear()
Response.ClearHeaders()
Response.ClearContent()
Response.Buffer = True
Response.AddHeader("content-disposition", "attachment;filename=Data.csv")
Response.Charset = Encoding.UTF8.WebName
Response.ContentType = "text/csv"
For Each OneLine As String In MyOutList
Response.Write(OneLine & vbCrLf)
Next
Response.Flush()
Response.End()
试试上面的方法 - 它 运行 哦,快多了!!!
然后post回到这里需要多长时间!!!
因此,我们需要逐行处理数据库,但不要使用我们一遍又一遍连接的巨大字符串。
我使用 this 指南在 VS 2019 中构建了一个 ASP.NET Web 表单,带有一个按钮,该按钮在我的 SQL 服务器上执行存储过程,并将结果写入 CSV通过浏览器下载文件。
一切都很好,除了它非常慢。
例如,它花了一个多小时才生成一个 3.4 MB 的 CSV,其中包含 20K 行。
有什么办法可以加快速度吗?
这是我的代码,因为我确实根据搜索此问题时找到的内容稍作修改,但 none 不幸的是,它有所帮助:
Protected Sub ExportCSV(ByVal sender As Object, ByVal e As EventArgs)
Dim sqlcmd As String = "EXEC my_sp_name;"
Dim constr As String = ConfigurationManager.ConnectionStrings("CONNSTR").ConnectionString
Using con As New SqlConnection(constr)
Using cmd As New SqlCommand(sqlcmd)
Using sda As New SqlDataAdapter()
cmd.Connection = con
sda.SelectCommand = cmd
Using dt As New DataTable()
sda.Fill(dt)
Dim csv As String = String.Empty
For Each column As DataColumn In dt.Columns
csv += column.ColumnName + ","
Next
csv += vbCrLf
For Each row As DataRow In dt.Rows
For Each column As DataColumn In dt.Columns
csv += row(column.ColumnName).ToString().Replace(",", ";") + ","
Next
csv += vbCrLf
Next
Response.Clear()
Response.ClearHeaders()
Response.ClearContent()
Response.Buffer = True
Response.AddHeader("content-disposition", "attachment;filename=Data.csv")
Response.Charset = Encoding.UTF8.WebName
Response.ContentType = "text/csv"
Response.Output.Write(csv)
Response.Flush()
Response.End()
End Using
End Using
End Using
End Using
End Sub
我的 SQL 服务器上没有 activity,而它是 运行。我什至可以在我的 SQL 服务器上用相同的参数在几秒钟内手动执行相同的存储过程。
我不知道该怎么办。因此,非常感谢任何帮助。
好的,有一个简单的解释来解释为什么 运行 这么慢。事实上,您可以使 运行 快几百万倍!
首先: 数据库和索引? 20,000 行不是问题。事实上,如果您使用在垃圾箱中找到的旧计算机?它将很容易每秒拉取 100,000 行 - 并且在没有任何索引的情况下对数据库执行此操作!! - 即使您应用排序和标准。
所以,不,这不是数据库问题 - 您的速度非常快,实际上您需要一些努力才能使数据库 运行 变慢。
那么,那到底是什么问题呢?
为什么当然是字符串拼接!!!!
这样想:
假设你要走 150 英里
但是,说什么时候达到 100 英里?去第 101 英里?
嗯,你回到1英里,然后走100英里+1英里。
然后是 102 英里?
您回到第 1 英里,然后步行 102 英里
然后是 103 英里?
您回到第 1 英里,然后步行 103 英里
请注意,仅仅 100 到 103,您现在已经走了 300 多英里!!!
您的字符串连接出现相同情况。事实上,在您的字符串达到大约 2000 个左右的字符后,您会看到速度大幅下降。
那么,假设您的字符串现在有 500,000 个字符?
然后你去
MyString = MyString & "Hello"
它有什么作用?
为什么它从字符位置 1 开始,经过 500,000 个字符,然后添加第二部分。
所以,这就像走路的问题一样!!!
顺便说一句,最有趣的是有些人建议检查数据库速度。 事实上,如果你去 Microsoft 或 Google?
这样的地方面试他们会问一大堆问题,以您作为开发人员为中心,回答有关多大、多远、多少的问题。
换句话说,他们正在寻找具有规模感的开发人员。
我的意思是,您可以步行到 1 个街区外的商店。但是 10 英里以外呢?不,你只能在一天内实际做到这一点! (到商店 10 英里,返回 10 英里)。因此,您需要使用某种交通工具。 (而且您需要意识到您不能再步行去商店了 - 可以吗??)。
可爱的小问题 post 这里是一个完美的例子,就是需要这种简单的思考来解释为什么 运行 这么慢!
事实上,删除所有数据库和所有文件操作。
试试这个简单的循环:
Dim str1 As String = ""
Dim str2 As String = Space(500)
Dim t As Long = Date.Now.Ticks
Dim i As Integer
For i = 1 To 3000
str1 = str1 + str2
Next
Dim td As Double = (Date.Now.Ticks - t) / 10000 / 1000
TextBox1.Text = td
那么,到 3000 的简单循环?
(您的计算机可以 for/next 在不到 1 秒的时间内循环到 10 亿!!!)
大约需要 10 秒(在较慢的计算机上)。
但是,现在让我们尝试 6000。(但请记住,您每次都回到起点,并且每次都走得更远才能到达终点)。它不是线性增长过程 - 而是指数增长!!!!
那么,需要 20 秒吗? 不,因为每次,你都会回到起点,遍历所有的终点,然后添加第二个字符串。而且每走一步都走得更远(更长)!!!
那么,6000?应该需要 20 秒,对吗? 不!!!
我们得到:48 秒!!! -- 时间增长呈指数增长!!!1
现在你知道为什么那些招聘公司会问上面那种求职面试问题了吧!!! - 多高,多远,多大!!!您必须能够根据这些类型的问题进行思考。
那么,如果我们创建新的数据行,但将其“添加”为一个数组,甚至是一个列表(这样就很容易将其作为行写到文件中),会发生什么情况。
现在让我们试试这个:
Dim str2 As String = Space(500)
Dim myList As List(Of String) = New List(Of String)
Dim t As Long = Date.Now.Ticks
Dim i As Integer
For i = 1 To 6000
myList.Add(str2)
Next
Dim td As Double = (Date.Now.Ticks - t) / 10000 / 1000
TextBox1.Text = td
时间:0
它运行速度如此之快,以至于它甚至都没有注册。
让我们转到 100,000 行。
例如:
For i = 1 To 100000
myList.Add(str2)
Next
时间:0.005033
5 1/1000 秒!
请注意这是多么惊人的速度!!!! - 它不仅是 1000 倍,而且速度快了 100 倍 运行!!!!
那么,对于您的代码?好吧,既然上面的速度非常快,那么我们可以在这里偷懒一点,甚至不必创建一行并将其写出来。因为我们正在处理 20,000 行?这么小,又是为了节省时间和简化代码?
我们可以用上面的。但是,如果这是 50k 或 100k 行呢?那么是的,我建议主循环创建行,然后将其发送到文件。
但是,我们可以这样说:(空气代码如下!!!)
Dim MyOutList As List(Of String) = New List(Of String)
Dim csv As String = ""
For Each column As DataColumn In dt.Columns
csv += column.ColumnName + ","
Next
MyOutList.Add(csv)
For Each row As DataRow In dt.Rows
csv = ""
For Each column As DataColumn In dt.Columns
csv += row(column.ColumnName).ToString().Replace(",", ";") + ","
Next
MyOutList.Add(csv)
Next
Response.Clear()
Response.ClearHeaders()
Response.ClearContent()
Response.Buffer = True
Response.AddHeader("content-disposition", "attachment;filename=Data.csv")
Response.Charset = Encoding.UTF8.WebName
Response.ContentType = "text/csv"
For Each OneLine As String In MyOutList
Response.Write(OneLine & vbCrLf)
Next
Response.Flush()
Response.End()
试试上面的方法 - 它 运行 哦,快多了!!!
然后post回到这里需要多长时间!!!
因此,我们需要逐行处理数据库,但不要使用我们一遍又一遍连接的巨大字符串。