我是不耐烦还是效率低下?
Am I being impatient or not Efficient?
所以快速总结一下我有一个 sqlServer
数据库(使用 SSMS 来处理它)并且它有一个 table 和 order_num 列,然后是 description 列。
例如 16548221587 | Small hairbrush.
我在 order_num 列上有索引。
然后我有一个 VB.net 应用程序,基本上我希望它允许用户放置一个包含大量 order_nums[=41= 列表的 .txt 文件](>150,000,每行 1 个),它的作用是逐行读取这些内容,然后搜索数据库,将其全部添加到临时 table,然后 streamwrites
添加到 "Results" .txt 文件。
关于这个问题的标题,我问它是因为我将在下面 post 编写的代码有效!我在阅读和查找时计时,并以 .427 秒的搜索时间将每个查找插入到临时 table 中,但将其与 150,000 条记录放在一起查看需要 16 多个小时!所以这就是我想知道的,我是在以一种颠倒的方式做这件事,还是我对 reading/finding 的期望太多了,并检索了那么多记录并希望它更快?
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
更新
我已经采纳了一些这方面的建议,现在我 sql 将需要搜索的 order_num 批量复制到 Temp table 中,这完成得相当快。然后我使用
之类的查询
SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num
但它似乎仍然很慢,即使只有 170 个结果也需要大约 30 秒,这在我看来是相当慢的。我在 order_table 中的 order_num 上放置了聚簇索引,在临时 table 上放置了没有索引,除了 sql table 之外,它基本上只是 .txt 文件
更新 2
就像我说的,我现在在 OrderTable 上有一个 non-clustered 索引(orderNo 包括描述),在 TempTable 上有一个聚集索引(Order_num),但是任何类型的连接或交叉应用等。仍然需要超过 33 秒才能基本上加入 100 个 OrderNum 和 return 只是 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
已解决
所以最后一点是我是个白痴,当我制作临时 table 时,你们基本上都是正确的,我不小心将 Column 设为 nvarchar,而在实际的 OrderTable 中,它只是 [=52 的 varchar 列=].抱歉那些家伙我半睡半醒!
我不知道你在命令中使用的存储过程的结构,但我想大多数时候 SQL 服务器上的操作以及应用程序与 SQL服务器占用大部分时间。所以考虑那里的优化——比如一次将所有数字发送给程序。您不必调用过程 xy 次。
并尝试查看存储过程的执行计划 - 是否有可以改进的地方?
我建议进行以下优化。
首先,Bulk insert
直接从 txt 文件中输入临时订单 ID table。如果你在每一行中有一个 order_num
那么这样就可以了。
BULK INSERT TempOrderIdTable
FROM 'C:\data\orderids.txt'
WITH (FIRSTROW = 1,
FIELDTERMINATOR = ',',
ROWTERMINATOR='\n' );
要使 bulk insert
正常工作,文件必须可供 SQL 服务器访问,可以在 SQL 服务器机器上或通过网络上的共享位置访问。
其次,运行 通过加入临时订单 ID table:
一次性获取所有描述的查询
SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num
现在分两步得到结果。
您的代码存在问题,您执行了此 SQL 命令 15 万次。那永远行不通(快)。
您可以做的是先从文件中读取 150k 个值,然后使用 SqlBulkCopy
into a table, for example as shown in this SO answer 插入。基本上做你的 dbo.OrdersToTemp
程序所做的,在你的 vb.net 代码中 - 一次而不是一次一行。考虑到您当前查询的延迟,这最多需要几秒钟。
对于以下查询:
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服务器版本支持,你可以用WITH (DATA_COMPRESSION = PAGE)
压缩索引,但这需要企业许可证(或开发者,但你不能在生产环境中使用) .
仅供将来参考(批量复制和索引肯定是答案)...
不要为循环的每次迭代创建命令,在每次迭代中添加参数并设置命令的属性。它们对于每个循环都是相同的;只有参数的值发生变化。
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
所以快速总结一下我有一个 sqlServer
数据库(使用 SSMS 来处理它)并且它有一个 table 和 order_num 列,然后是 description 列。
例如 16548221587 | Small hairbrush.
我在 order_num 列上有索引。
然后我有一个 VB.net 应用程序,基本上我希望它允许用户放置一个包含大量 order_nums[=41= 列表的 .txt 文件](>150,000,每行 1 个),它的作用是逐行读取这些内容,然后搜索数据库,将其全部添加到临时 table,然后 streamwrites
添加到 "Results" .txt 文件。
关于这个问题的标题,我问它是因为我将在下面 post 编写的代码有效!我在阅读和查找时计时,并以 .427 秒的搜索时间将每个查找插入到临时 table 中,但将其与 150,000 条记录放在一起查看需要 16 多个小时!所以这就是我想知道的,我是在以一种颠倒的方式做这件事,还是我对 reading/finding 的期望太多了,并检索了那么多记录并希望它更快?
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
更新
我已经采纳了一些这方面的建议,现在我 sql 将需要搜索的 order_num 批量复制到 Temp table 中,这完成得相当快。然后我使用
之类的查询SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num
但它似乎仍然很慢,即使只有 170 个结果也需要大约 30 秒,这在我看来是相当慢的。我在 order_table 中的 order_num 上放置了聚簇索引,在临时 table 上放置了没有索引,除了 sql table 之外,它基本上只是 .txt 文件
更新 2
就像我说的,我现在在 OrderTable 上有一个 non-clustered 索引(orderNo 包括描述),在 TempTable 上有一个聚集索引(Order_num),但是任何类型的连接或交叉应用等。仍然需要超过 33 秒才能基本上加入 100 个 OrderNum 和 return 只是 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
已解决 所以最后一点是我是个白痴,当我制作临时 table 时,你们基本上都是正确的,我不小心将 Column 设为 nvarchar,而在实际的 OrderTable 中,它只是 [=52 的 varchar 列=].抱歉那些家伙我半睡半醒!
我不知道你在命令中使用的存储过程的结构,但我想大多数时候 SQL 服务器上的操作以及应用程序与 SQL服务器占用大部分时间。所以考虑那里的优化——比如一次将所有数字发送给程序。您不必调用过程 xy 次。 并尝试查看存储过程的执行计划 - 是否有可以改进的地方?
我建议进行以下优化。
首先,Bulk insert
直接从 txt 文件中输入临时订单 ID table。如果你在每一行中有一个 order_num
那么这样就可以了。
BULK INSERT TempOrderIdTable
FROM 'C:\data\orderids.txt'
WITH (FIRSTROW = 1,
FIELDTERMINATOR = ',',
ROWTERMINATOR='\n' );
要使 bulk insert
正常工作,文件必须可供 SQL 服务器访问,可以在 SQL 服务器机器上或通过网络上的共享位置访问。
其次,运行 通过加入临时订单 ID table:
一次性获取所有描述的查询SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num
现在分两步得到结果。
您的代码存在问题,您执行了此 SQL 命令 15 万次。那永远行不通(快)。
您可以做的是先从文件中读取 150k 个值,然后使用 SqlBulkCopy
into a table, for example as shown in this SO answer 插入。基本上做你的 dbo.OrdersToTemp
程序所做的,在你的 vb.net 代码中 - 一次而不是一次一行。考虑到您当前查询的延迟,这最多需要几秒钟。
对于以下查询:
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服务器版本支持,你可以用WITH (DATA_COMPRESSION = PAGE)
压缩索引,但这需要企业许可证(或开发者,但你不能在生产环境中使用) .
仅供将来参考(批量复制和索引肯定是答案)... 不要为循环的每次迭代创建命令,在每次迭代中添加参数并设置命令的属性。它们对于每个循环都是相同的;只有参数的值发生变化。
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