【发布时间】:2020-09-18 13:34:42
【问题描述】:
我尝试使用 C# 从 SQL Server 数据库中获取大量数据。我从数据库中得到了大约 30 万行数据(我相信这离最坏的情况不远了。)可能包含数亿。
我认为问题不在于数据库的大小,因为
的command.ExecuteReader(); 大约只需要不到一秒钟。
我试过这段代码:
public List<ResultPulser> GetReportResult(SqlConnection opCon, SqlCommand command,
int minReport,int maxReport,int machineNumber)
{
List<ResultPulser> results = new List<ResultPulser>();
using (DataContext dc = new DataContext(opCon))
{
try
{
command.CommandText = "select * from ResultPulser " +
"where CAST(SUBSTRING([ReportNumber], 0, 8) as int) = @machineNumber and " +
"CAST(SUBSTRING([ReportNumber],8,LEN([ReportNumber])) as int) BETWEEN @minReport AND @maxReport";
command.Parameters.Clear();
command.Parameters.AddWithValue("@minReport", minReport);
command.Parameters.AddWithValue("@maxReport", maxReport);
command.Parameters.AddWithValue("@machineNumber", machineNumber);
Stopwatch SW1 = Stopwatch.StartNew();
SqlDataReader reader = command.ExecuteReader();
SW1.Stop();
DataTable table = new DataTable();
Stopwatch SW2 = Stopwatch.StartNew();
table.Load(reader);
SW2.Stop();
Stopwatch SW3 = Stopwatch.StartNew();
ResultPulser[] report = new ResultPulser[table.Rows.Count];
for (int i = 0; i < table.Rows.Count; i++)
{
DataRow dr = table.Rows[i];
report[i] = new ResultPulser(Convert.ToInt64(dr[0]), dr[1].ToString().Trim(),
dr[2].ToString().Trim(), Convert.ToDateTime(dr[3]), Convert.ToDouble(dr[4]),
Convert.ToDouble(dr[5]), Convert.ToDouble(dr[6]), Convert.ToDouble(dr[7]),
Convert.ToDouble(dr[8]), Convert.ToDouble(dr[9]), Convert.ToInt64(dr[10]),
Convert.ToInt64(dr[11]), Convert.ToInt64(dr[12]), Convert.ToBoolean(dr[13]),
Convert.ToInt32(dr[14]));
}
SW3.Stop();
reader.Close();
return report.ToList();
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
throw ex;
}
}
}
但下一行 table.Load(reader); 大约需要 20 秒才能完成。
我也试过这样:
public List<ResultPulser> GetReportResult(SqlConnection opCon, SqlCommand command,
int minReport,int maxReport,int machineNumber)
{
List<ResultPulser> results = new List<ResultPulser>();
using (DataContext dc = new DataContext(opCon))
{
try
{
command.CommandText = "select * from ResultPulser " +
"where CAST(SUBSTRING([ReportNumber], 0, 8) as int) = @machineNumber and " +
"CAST(SUBSTRING([ReportNumber],8,LEN([ReportNumber])) as int) BETWEEN @minReport AND @maxReport";
command.Parameters.Clear();
command.Parameters.AddWithValue("@minReport", minReport);
command.Parameters.AddWithValue("@maxReport", maxReport);
command.Parameters.AddWithValue("@machineNumber", machineNumber);
Stopwatch SW1 = Stopwatch.StartNew();
SqlDataReader reader = command.ExecuteReader();
SW1.Stop();
DataTable table = new DataTable();
Stopwatch SW2 = Stopwatch.StartNew();
while (reader.Read())
{
results.Add(new ResultPulser(reader.GetInt64(0), reader.GetString(1).Trim(), reader.GetString(2).Trim(),
reader.GetDateTime(3), reader.GetDouble(4), reader.GetDouble(5), reader.GetDouble(6),
reader.GetDouble(7), reader.GetDouble(8), reader.GetDouble(9), reader.GetInt64(10),
reader.GetInt64(11), reader.GetInt64(12), reader.GetBoolean(13), reader.GetInt32(14)));
}
SW2.Stop();
reader.Close();
return results;
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
throw ex;
}
}
}
在这种情况下,此代码部分大约需要 17-16 秒...
while (reader.Read())
{
results.Add(new ResultPulser(reader.GetInt64(0), reader.GetString(1).Trim(), reader.GetString(2).Trim(),
reader.GetDateTime(3), reader.GetDouble(4), reader.GetDouble(5), reader.GetDouble(6),
reader.GetDouble(7), reader.GetDouble(8), reader.GetDouble(9), reader.GetInt64(10),
reader.GetInt64(11), reader.GetInt64(12), reader.GetBoolean(13), reader.GetInt32(14)));
}
如何优化我的代码以使其更快?
【问题讨论】:
-
创建 300,000 个对象的列表后,您将如何处理它?
-
第一个问题,正如 Caius 上面所说的,真的,你需要一次性将这么多数据加载到内存中吗?如果答案是肯定的,那么将这么多数据传输到您的程序中需要真正可衡量的时间。
-
确实,我问'cos,如果答案是“我要把它们放在一个 ComboBox 中,让用户选择一个!”那么您需要查看您的 UI/UX 要求。如果是“我要将它们写入磁盘/将它们发送到套接字”,那么答案是直接流式传输它们,一次一个,而不是将它们加载到内存中。很少有充分的理由在内存中拥有如此庞大的数据集。如果每个对象都是 1 kb,那么它是 300meg 加上仅用于列表。另外,您知道 list 在内部使用大小为 16 的数组并在需要更多空间时将其加倍(复制每个元素)吗?数百万无用的复制操作..
-
这太可笑了。您已经说过查询很快;实施我建议的改进,这样您就不会削弱报告编号上的索引,确保报告编号上有一个索引,并经常查询数据库以获取您需要的小数据项。不要仅仅因为您可能需要几千个并且不想进行一百个数据库查询就下载 300,000 个项目。还要考虑您的用户将如何处理他的 300,000 行报告;大概总结一下,因为谁想要/可以查看 300,000 行数据并理解它。这整个事情是一个半生不熟的XY问题..
-
(并考虑到,在上下文中,您的用户将花费几分钟或几小时来查看 300,000 行,生成数据的 7 秒是沧海一粟。节省服务器资源并将它们写入一个接一个的文件;你不需要所有这些在内存中。如果你在c#中汇总数据,请使用SQL在数据库中汇总它,这样X百兆字节就不会通过网络传输——可能是最慢的部分)
标签: c# sql sql-server optimization