【发布时间】:2016-02-24 04:49:36
【问题描述】:
这是迄今为止我发现的从 Oracle DB 检索响应记录集并将其写入分隔文件的最快方法。更快会更好。请提出建议。
检索结果集:
using (var oracleConnection = new OracleConnection(ContextInfo.ConnectionString))
{
oracleConnection.Open();
try
{
using (var oracleCommand = new OracleCommand(extractToRun, OracleConnection))
{
oracleCommand.CommandType = CommandType.StoredProcedure;
oracleCommand.BindByName = true;
oracleCommand.FetchSize = oracleCommand.FetchSize * 128;
oracleCommand.InitialLONGFetchSize = 5000;
oracleCommand.Parameters.Add(refCursorOracleParameter);
oracleCommand.Parameters.Add(startDateOracleParameter);
oracleCommand.Parameters.Add(endDateOracleParameter);
oracleCommand.Parameters.Add(jobIdOracleParameter);
using (var oracleDataAdapter = new OracleDataAdapter(oracleCommand))
{
oracleDataAdapter.Fill(ds);
return ds;
}
}
}
finally
{
oracleConnection.Close();
oracleConnection.Dispose();
}
}
处理数据并将其写入文件:
public static void ExportDataTableToDelimitedFile(DataTable table, string filename, string encloseWith, string delimiter, bool includeHeader, string fieldsToExclude, bool fixedLengthValues)
{
String excludeList = String.Empty;
if (!String.IsNullOrEmpty(fieldsToExclude))
{
excludeList = fieldsToExclude.ToUpper();
}
using (FileStream fs = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 131072, FileOptions.None))
{
BinaryWriter sw = new BinaryWriter(fs);
if (table.Rows.Count == 0)
{
sw.Write(String.Empty);
sw.Close();
sw.Dispose();
return;
}
//Handle header
if (includeHeader)
{
string header = String.Empty;
String formattedHeader = String.Empty;
foreach (DataColumn clm in table.Columns)
{
if (excludeList.Contains(clm.ColumnName.ToUpper()))
continue;
if (clm.ColumnName.Length > 0)
{
formattedHeader = String.Empty;
formattedHeader = encloseWith + clm.ColumnName + encloseWith;
if (header.Length > 0)
header = String.Join(delimiter, new string[] { header, formattedHeader });
else
header = formattedHeader;
}
}
sw.Write(header);
}
// handle values in data rows now
Boolean hasEnlosedCharacter = !String.IsNullOrEmpty(encloseWith);
ParallelOptions rowOptions = new ParallelOptions();
rowOptions.MaxDegreeOfParallelism = Environment.ProcessorCount;
Parallel.ForEach(table.Rows.Cast<DataRow>(), rowOptions, row =>
{
char[] rowValue = new char[8192];
Int32 rowValueIndex = 0;
string[] dcc = row.ItemArray.Select(field => field.ToString()).ToArray();
foreach (String dc in dcc)
{
if (rowValueIndex > 0)
{
if (!String.IsNullOrEmpty(dc) && hasEnlosedCharacter)
{
rowValue[rowValueIndex++] = delimiter[0];
rowValue[rowValueIndex++] = encloseWith[0];
foreach (char c in dc)
{
rowValue[rowValueIndex++] = c;
}
rowValue[rowValueIndex++] = encloseWith[0];
}
else
{
rowValue[rowValueIndex++] = delimiter[0];
foreach (char c in dc)
{
rowValue[rowValueIndex++] = c;
}
}
}
else
{
if (!String.IsNullOrEmpty(dc) && hasEnlosedCharacter)
{
rowValue[rowValueIndex++] = encloseWith[0];
foreach (char c in dc)
{
rowValue[rowValueIndex++] = c;
}
rowValue[rowValueIndex++] = encloseWith[0];
}
else
{
foreach (char c in dc)
{
rowValue[rowValueIndex++] = c;
}
}
}
}
rowValue[rowValueIndex++] = '\r';
rowValue[rowValueIndex++] = '\n';
lock (sw)
{
sw.Write(rowValue, 0, rowValueIndex);
}
});
sw.Close();
sw.Dispose();
table.Dispose();
fs.Close();
}
}
我知道我应该重命名一些变量并以相同的方式处理标题(我不是在写标题)所以这确实是一个纯粹的逻辑问题,风格的答案无助于提高性能。
令人费解的是网络性能。当它快速返回 5 个几千行的数据集时,它只使用了 1.5% 的带宽?我正在对 11g 数据库使用最新的 ODP.Net (Oracle)。我尝试了 Devarts 提供程序,但它完全被我炸毁了。
处理器负载反映了 Parallel.ForEach 对数据表中的行的影响,这是一件好事。
【问题讨论】:
-
我建议使用using statement 而不是自己处理对象,这样更不容易出错。
-
1) 你没有显示你的查询,所以我不能评论它的效率。 2) 您没有显示
DataTable是如何从ds变量中填充的,您没有显示该变量被实例化。 3)我不知道为什么在数据检索后使用您的fieldToExclude变量而不是使用它来创建有效的查询。有了这些限制,您是否尝试过使用StreamWriter? -
实际上,在大多数情况下,从 oracle DB 中检索数据的时间应该比任何与后续处理数据表中的数据有关的操作都高出一个数量级。那么你真正感兴趣的是什么? (1) 将您的数据从 oracle 获取到数据表 (2) 将您的数据从数据表获取到文件 (3) 将您的数据从 oracle 获取到文件?
-
其实从Oracle检索数据的时间还不错。我正在并行运行 7 个任务以运行 7 个查询并将数据集返回到主线程。完成后,我调用上面列出的过程 (ExportDataTableToDelimitedFile) 将内容写入分隔文件。查询会在几秒钟内返回。不过,将数据集带回客户端确实需要时间。大约30秒。导出 413K 行的总时间为 50 秒。我们有一个客户,他将通过 DAILY 导出数百万行。
-
我已经尝试了多种将数据集转换为 char[] 数组的其他方法,这是可取的,因为二进制写入器是迄今为止最快的流写入器。它有一个 16 mb 的缓冲区。行 string[] dcc = row.ItemArray.Select(field => field.ToString()).ToArray();显着提高了速度,但没有办法将其转换为 char[][]?
标签: c# performance oracle11g