【问题标题】:Am I being impatient or not Efficient?我是不耐烦还是效率低下?
【发布时间】:2019-04-11 10:28:55
【问题描述】:

所以快速总结一下,我有一个 sqlServer 数据库(使用 SSMS 来处理它),它有一个带有 order_num 列和一个 description 列的表. 例如 16548221587 | Small hairbrush. 我在 order_num 列上有索引。

然后我有一个 VB.net 应用程序,基本上我希望它允许用户放置一个 .txt 文件,其中包含大量 order_nums 列表(>150,000,每行 1 个)及其内容确实是逐行读取这些,然后搜索数据库,将其全部添加到临时表中,然后将 streamwrites 它添加到“结果”.txt 文件中。

关于这个问题的标题,我问它是因为我将在下面发布的代码有效!我在阅读和查找并将每个查找插入到临时表中的时间为 0.427 秒搜索,但将其与 150,000 条记录一起查看它需要 16 多个小时!所以这就是我想知道我这样做是一种颠倒的方式还是我期望太多阅读/查找和检索那么多记录并期望它更快?

If System.IO.File.Exists(TextBox2.Text) Then
                    'read in file list of order numbers to search
                    result = From n In System.IO.File.ReadLines(TextBox2.Text)
                             Where n.Length = 13
                             Select n.Substring(0, 13)
                Else
                    MessageBox.Show("The file path you entered seems to be invalid. Please try again.")
                    Exit Sub
                End If



                For Each word As String In result

                    Dim cmd As New SqlCommand("dbo.OrdersToTemp", con)
                    cmd.CommandType = CommandType.StoredProcedure
                    cmd.Parameters.Add("@OrderNum", SqlDbType.NVarChar).Value = word.ToString()
                    cmd.CommandTimeout = 3000
                    cmd.ExecuteNonQuery()

                Next




                Using sw As New StreamWriter(TextBox2.Text.Substring(0, TextBox2.TextLength - 4) + "-results.txt")


                    Dim retrieveResults As New SqlCommand("dbo.GetResults", con)
                    retrieveResults.CommandType = CommandType.StoredProcedure
                    retrieveResults.CommandTimeout = 3000

                    Using RDR = retrieveResults.ExecuteReader

                        Do While RDR.Read



                            OrderDescription = RDR("Description").ToString()

                            sw.WriteLine(OrderDescription )

                        Loop

                    End Using




                End Using

更新

我已经对此提出了一些建议,现在我将需要搜索的 order_nums sqlbulkcopy 到 Temp 表中,这完成得非常快。然后我正在使用诸如

之类的查询
SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num

但是即使只有 170 个结果也需要 30 秒,这在我看来还是很慢的。我在 order_table 中的 order_num 上放置了一个聚集索引,而在 temp 表上放置了 NO 索引,它基本上只是 .txt 文件,除了在 sql 表中

更新 2

所以就像我说的,我现在在 OrderTable 上有一个非聚集索引(orderNo 包含描述),在 TempTable 上有一个聚集索引(Order_num),但是任何类型的连接或交叉应用等仍然需要 33 秒以上才能基本上加入100 OrderNum 并仅返回 170,这仍然很慢。这是我正在尝试的连接:

select o.Order_num, t.description
from Temp_data o
join OrderTable on t.Order_num= o.Order_num


select x.Order_num, x.description
from OrderTable x
where Order_num in (select Order_num from Temp_data)


select x.Order_num,x.description
from OrderTable x
cross apply (select o.Order_num from Temp_data o where o.Order_num= x.Order_num) ord

已解决 所以最后一点是我是个白痴,当我制作临时表时,你基本上都是正确的,我不小心将列设置为 nvarchar,而在实际的 OrderTable 中,它只是 order_num 的 varchar 列。对不起,我半睡半醒!

【问题讨论】:

  • 索引该临时表的 order_num 列。

标签: sql sql-server vb.net streamreader streamwriter


【解决方案1】:

您的代码的问题是您执行此 SQL 命令 150k 次。那永远不会奏效(快)。

您可以先从文件中读取 150k 值,然后使用 SqlBulkCopy 将其插入到表 for example as shown in this SO answer 中。基本上在您的 vb.net 代码中执行您的 dbo.OrdersToTemp 程序所做的事情 - 一次完成,而不是一次一行。考虑到您当前查询的延迟,这最多需要几秒钟。

对于以下查询:

SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num

我假设OrderTable 可以包含每个order_num 的多条记录(您提到为 100 个订单返回 170 行),您可以这样使用索引:

CREATE INDEX ix ON OrderTable (order_num) INCLUDE (description)

如果您在文件中的订单号是唯一的(并且您可以确保唯一性):

CREATE UNIQUE CLUSTERED INDEX ux ON TempOrderIdTable (order_num);

如果您的 SQL Server 版本支持它,您可以使用 WITH (DATA_COMPRESSION = PAGE) 压缩索引,但这需要 Enterprise 许可证(或 Developer,但您不能在 prod 环境中使用它)。

【讨论】:

  • 确实很慢,OrderTable有多大?!有关索引的建议,请参阅我的更新答案。
  • 大约有 1000 万行!
  • @MarcinJ 临时表上的索引应该可以解决问题,SqlBulkCopy 比共享文件更好。我可以建议您将连接查询添加到答案中以完成它并让 OP 接受您的答案吗?
  • 嗨@MarcinJ!所以我完全按照你说的做了,添加了确切的索引,除了运行和获取查询结果之外的所有操作仍然需要 35 秒(对于 100 张票,170 个结果)!我确实有一种感觉,这是我忘记提及的事情(道歉)是描述是nvarchar(max),因为它们有时可能很长,所以可能会影响它?
【解决方案2】:

我不知道你在命令中使用的存储过程的结构,但我猜大部分时间是SQL服务器上的操作以及应用程序与SQL服务器之间的通信占用了大部分时间.因此,请考虑那里的优化-例如一次将所有数字发送到程序。您不必调用过程 xy 次。 并尝试查看存储过程的执行计划——有什么可以改进的吗?

【讨论】:

    【解决方案3】:

    我建议进行以下优化。

    首先,将Bulk insert直接从txt文件中放入临时订单id表中。如果每一行都有一个order_num,那么就可以了。

    BULK INSERT TempOrderIdTable
    FROM 'C:\data\orderids.txt'
    WITH (FIRSTROW = 1,
        FIELDTERMINATOR = ',',
        ROWTERMINATOR='\n' );
    

    要使bulk insert 工作,SQL Server 必须可以访问该文件,无论是在 SQL Server 机器上还是通过网络上的共享位置。

    其次,通过加入临时订单 id 表运行查询以一次性获取所有描述:

    SELECT o.order_num, description
    from OrderTable o
    join TempOrderIdTable t on t.order_num = o.order_num
    

    现在您分两步获得结果。

    【讨论】:

    • 这只有在他们可以将文件获取到运行 SQL Server 的服务器时才有效。
    • @Marcin 或者它需要能够被 SQL 服务器机器通过网络路径访问。好点子;让我们看看OP怎么说。
    • 大家好,所以我使用数据表做了我自己的 sqlbulkcopy 版本,所有这些都可以立即运行或至少非常快,然后我使用整个连接查询,但甚至执行 100 个 order_numbers(return 170) 仍然需要大约 30 秒,这对于连接查询来说相当慢,有什么想法吗?
    【解决方案4】:

    仅供将来参考(批量复制和索引肯定是答案)... 不要为循环的每次迭代创建命令,在每次迭代中添加参数并设置命令的属性。它们对于每个循环都是相同的;只有参数的值会改变。

        Dim cmd As New SqlCommand("dbo.OrdersToTemp", con)
        cmd.CommandType = CommandType.StoredProcedure
        cmd.Parameters.Add("@OrderNum", SqlDbType.NVarChar)
        cmd.CommandTimeout = 3000
        For Each word As String In Result
            cmd.Parameters("@OrderNum").Value = word
            cmd.ExecuteNonQuery()
        Next
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-04
      • 1970-01-01
      • 1970-01-01
      • 2016-06-28
      • 1970-01-01
      • 2012-06-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多