C# 从SqlDataReader读取JSON字符串的最有效和最快速的方法
我试图从SQL Server 2016数据库返回一个相当大的数据集(数千行),格式为JSON(使用SQL Server 2016的FOR JSON自动功能);但是,我在从SqlDataReader读取结果时遇到了问题 我之前将结果作为常规行加载到DataTable中,这非常有效(加载整个表大约需要10-15秒)。但是,如果我尝试使用相同的数据构建作为JSON返回的字符串,那么构建从DataReader返回的字符串需要几分钟的时间。我想补充一点,在这个查询中,我还返回了大量的二进制数据(我有SqlGeometry对象,我正试图从数据库中检索)。我从中返回的典型字符串有几个100k字符长,读卡器的读取速度非常慢 无论如何,我解析JSON的加载代码如下:C# 从SqlDataReader读取JSON字符串的最有效和最快速的方法,c#,json,sqldatareader,C#,Json,Sqldatareader,我试图从SQL Server 2016数据库返回一个相当大的数据集(数千行),格式为JSON(使用SQL Server 2016的FOR JSON自动功能);但是,我在从SqlDataReader读取结果时遇到了问题 我之前将结果作为常规行加载到DataTable中,这非常有效(加载整个表大约需要10-15秒)。但是,如果我尝试使用相同的数据构建作为JSON返回的字符串,那么构建从DataReader返回的字符串需要几分钟的时间。我想补充一点,在这个查询中,我还返回了大量的二进制数据(我有Sql
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);
}
public static Task ExecuteJsonReaderAsync(string connectionString,CommandType CommandType,string commandText,SqlParameter[]oParams=null,int timeout=30,CancellationToken=default(CancellationToken))
{
返回Task.Factory.StartNew(()=>
{
var str=string.Empty;
使用(var连接=新的SqlConnection(connectionString))
{
connection.Open();
使用(var命令=新的SqlCommand(commandText,connection))
{
command.CommandTimeout=超时;
command.CommandType=CommandType;
if(oParams?.Length>0)命令.Parameters.AddRange(oParams);
使用(var reader=command.ExecuteReader(CommandBehavior.CloseConnection))
{
while(reader.Read())
{
str=$“{str}{reader[0]}”;
}
reader.Close();
}
返回str;
}
}
},代币);
}
我尝试了各种命令选项来加速它,包括CloseConnection、SequentialAccess、SingleResult,但都没有成功。为什么构建字符串比从相同数据加载数据表花费的时间要长得多,有没有更快的方法来实现这一点
我想这一定是我做错了什么,或者是我忽略了什么,我希望有人以前遇到过这个问题。有什么想法吗?您的代码在每个循环中重新分配内存中的字符串变量。这对代码的性能是有害的。相反,该类有一个内部缓冲区,允许更少的内存重新分配,如果您知道数据的总长度,还可以控制该缓冲区的大小以避免重新分配 所以
更多关于这里有几个问题:
$“{str}{reader[0]}”
使用字符串格式对GC的性能影响很大。您应该使用StringBuilder
和Append()
方法async
,就像这样:public static async Task ExecuteJsonReaderAsync(string connectionString,CommandType CommandType,string commandText,SqlParameter[]oParams=null,int timeout=30,CancellationToken=default(CancellationToken))
{
var str=string.Empty;
等待使用var connection=newsqlconnection(connectionString);
等待连接。OpenAsync(令牌);
等待使用var命令=新的SqlCommand(commandText,connection);
command.CommandTimeout=超时;
command.CommandType=CommandType;
if(oParams?.Length>0)命令.Parameters.AddRange(oParams);
var stringBuilder=新的stringBuilder(1024*1024);
使用var reader等待=等待命令.ExecuteReaderAsync(CommandBehavior.CloseConnection,令牌);
while(等待reader.ReadAsync(令牌))
{
追加(读取器[0]);
}
等待reader.CloseAsync();
返回stringBuilder.ToString();
}
使用StringBuilder实例我认为您的问题可能是str=$“{str}{reader[0]}”代码>。尝试使用StringBuilder
并在while循环中附加到它。否则每次都会创建一个新字符串(字符串是不可变的)。只是一个与您的主要问题无关的备注,但不要使用Task.Factory.StartNew
,因为您可以一直使用异步/等待。考虑使用<代码> AuthEntueAdErasyCnc/代码>西里尔,设置这种方式的原因是B/C,该代码在别处用于返回可以通过AuthExeCudieAdErasyc使用的任务。这正是做这项工作的一部分。那么,字符串构建是什么导致它在while(reader.Read())循环中花费如此长的时间进行迭代呢?我不会想到使用不可变字符串会导致处理该循环花费那么长的时间。如果有一种更快的方法将所有结果转储到字符串变量中,那就太好了。默认情况下,如果json超过一定的长度,sql将返回多行。这是我不知道的一点,感谢您提出这一点。但是,数据的长度是不同的,所以我可以利用其中的一些数据,但无法分配总大小。我是否需要研究序列化选项以将数据读入sqlchars或sqlstring?必须有一种更快的方法将所有数据转储到单个字符串中。您不需要分配总大小。文件解释说
// Set an initial capacity of 1MB
StringBuilder str = new StringBuidler(1024*1024);
while (reader.Read())
{
str.Append(reader[0].ToString());
}
....
return str.ToString();
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();
}