我试图从格式化为JSON的SQL Server 2016数据库返回一个相当大的数据集(几千行)(使用SQL Server 2016的FOR JSON AUTO功能);但是,我遇到了从SqlDataReader读取结果的问题。
我之前将结果作为常规行加载到DataTable中,这非常有效(加载整个表大约需要10-15秒)。但是,如果我尝试使用相同的数据构建返回为JSON的字符串,则需要几分钟时间来构建从DataReader返回的字符串。我想补充一点,我也在这个查询中返回了大量的二进制数据(我有试图从数据库中检索的SqlGeometry对象)。我从这里返回的典型字符串是几十个字符长,读者的读数非常慢。
无论如何,我用于解析JSON的加载代码如下:
public static Task<string> ExecuteJsonReaderAsync(string connectionString, CommandType commandType, string commandText, SqlParameter[] oParams = null, int timeout = 30, CancellationToken token = default(CancellationToken))
{
return Task<string>.Factory.StartNew(() =>
{
var str = string.Empty;
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = new SqlCommand(commandText, connection))
{
command.CommandTimeout = timeout;
command.CommandType = commandType;
if (oParams?.Length > 0) command.Parameters.AddRange(oParams);
using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
str = $"{str}{reader[0]}";
}
reader.Close();
}
return str;
}
}
}, token);
}
我尝试过各种命令选项来尝试加速它,包括CloseConnection,SequentialAccess,SingleResult,但无济于事。为什么构建字符串比从相同数据加载DataTable要花费更长的时间,有没有更快的方法来实现这一点?
我认为这必须是我做错了或我忽略的事情,我希望有人之前遇到过这个问题。有什么想法吗?
答案 0 :(得分:2)
您的代码在每个循环中重新分配内存中的字符串变量。这不利于代码的性能。相反,类StringBuilder有一个内部缓冲区,允许更少的内存重新分配,你也可以控制这个缓冲区的大小,以避免重新分配,如果你知道数据的总长度。
所以
// Set an initial capacity of 1MB
StringBuilder str = new StringBuidler(1024*1024);
while (reader.Read())
{
str.Append(reader[0].ToString());
}
....
return str.ToString();
的更多信息
答案 1 :(得分:0)
这里有几个问题:
$"{str}{reader[0]}"
使用字符串格式对 GC 的性能影响很大。您应该使用 StringBuilder
和 Append()
方法。async
:以下是我将如何编写代码以获得更好的性能(通过异步缩放和通过 StringBuilder 进行内存缩放):
public static async Task<string> ExecuteJsonReaderAsync(string connectionString, CommandType commandType, string commandText, SqlParameter[] oParams = null, int timeout = 30, CancellationToken token = default(CancellationToken))
{
var str = string.Empty;
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(token);
await using var command = new SqlCommand(commandText, connection);
command.CommandTimeout = timeout;
command.CommandType = commandType;
if (oParams?.Length > 0) command.Parameters.AddRange(oParams);
var stringBuilder = new StringBuilder(1024 * 1024);
await using var reader = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection, token);
while (await reader.ReadAsync(token))
{
stringBuilder.Append(reader[0]);
}
await reader.CloseAsync();
return stringBuilder.ToString();
}