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回到这里需要多长时间!!!

因此,我们需要逐行处理数据库,但不要使用我们一遍又一遍连接的巨大字符串。