在程序中,我创建了以下逻辑,用于从数据库中读取数据并将其存储到List<>中:
NpgsqlCommand cmd = new NpgsqlCommand(query, conn);
List<UserInfo> result = new List<UserInfo>();
Npgsql.NpgsqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
string userId = rdr[0].ToString();
string sex = rdr[1].ToString();
string strDateBirth = rdr[2].ToString();
string zip = rdr[3].ToString();
UserInfo userInfo = new UserInfo();
userInfo.Msisdn = userId;
userInfo.Gender = sex;
try
{
userInfo.BirthDate = Convert.ToDateTime(strDateBirth);
}
catch (Exception ex)
{
}
userInfo.ZipCode = zip;
userInfo.DemographicsKnown = true;
userInfo.AgeGroup = getAgeGroup(strDateBirth);
if (result.Count(x => x.Id== userId) == 0)
result.Add(userInfo);
}
此代码的性能非常差。有超过2M的记录,半小时后,userInfo列表只包含300.000条记录。
有谁知道如何加速从数据库中读取数据?
答案 0 :(得分:3)
当你的意思是.Count
时,你正在使用.Any()
每当你打电话给.Count
时,你都在枚举整个集合,看看你是否只有一场比赛....
考虑你问的问题:
“你有多少行匹配这个条件?这个数字是否等于零?”
你真正的意思是:
“任何行都符合这个条件吗?”
在该上下文中,您可以创建userId值的Hashset。检查Hashset(或字典)中是否存在比检查列表中的相同要快得多。
此外,如果您执行已经拥有userId,那么您无缘无故地解析并读取所有值。首先检查myHashset.Contains(userId)
,然后添加。
这是它缓慢的主要原因。对于n行,您正在执行集合的第n个三角形枚举!
编辑:考虑这个未经测试的更改:我不知道您的读者是否支持类似GetString()
的类型读取方法,所以如果不支持,那么只需使用之前的内容。< / p>
NpgsqlCommand cmd = new NpgsqlCommand(query, conn);
List<UserInfo> result = new List<UserInfo>();
Npgsql.NpgsqlDataReader rdr = cmd.ExecuteReader();
HashSet<string> userHash = new HashSet<string>(); // is this actually an int?
while (rdr.Read())
{
string userId = rdr.GetString(0);
If (!userHash.Contains(userId))
{
string strDateBirth = rdrGetString(2);
UserInfo userInfo = new UserInfo();
userInfo.Msisdn = userId;
userInfo.Gender = rdr.GetString(1);
datetime parseddate; // this is not used if the parse fails
if (Datetime.TryParse(strDateBirth, out parseddate))
{
userInfo.BirthDate = parseddate;
// userInfo.AgeGroup = getAgeGroup(strDateBirth); // why take the string?
// rewrite your getAgeGroup method to take the datetime
userInfo.AgeGroup = getAgeGroup(parseddate);
}
userInfo.ZipCode = rdr.GetString(3);
userInfo.DemographicsKnown = true;
result.Add(userInfo);
userHash.Add(userId);
}
}
这将始终保留您找到的用户行的第一个实例(这是您当前代码所执行的操作)。如果您想保留最后实例,则可以使用字典并完全取消.Contains()
调用。
编辑:我刚注意到我的示例从未将用户ID添加到哈希...哎呀......将其添加到其中。
答案 1 :(得分:2)
所有这些execption处理都会使你的程序变慢。例外情况适用于 特殊情况 如果您的代码丢失了超过10个执行程序,则需要重新考虑您的设计。
而不是每次出现格式错误的日期时都使用DateTime.TryParse(string, DateTime)
代替执行。它会加速你的代码。
////Replace This
//try
//{
// userInfo.BirthDate = Convert.ToDateTime(strDateBirth);
//}
//catch (Exception ex)
//{
//}
//With this
DateTime bithDate;
if(DateTime.TryParse(strDateBirth, out bithDate)
{
userInfo.BirthDate = bithDate;
}
rdr[2]
列的数据类型是什么?它已经是DateTime吗?另一件事是停止在任何地方的对象上调用ToString并使用正确的方法。
while(rdr.Read())
{
UserInfo userInfo = new UserInfo();
userInfo.Msisdn = rdr.GetString(0);
userInfo.Gender = rdr.GetString(1);
DateTime? birthdate = null; //This is a nullable DateTime see http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx
if(rdr.IsDbNull() == false)
{
birthdate = rdr.GetDateTime(2);
userInfo.BirthDate = birthdate.Value;
}
userInfo.ZipCode = rdr.GetString(3);
userInfo.DemographicsKnown = true;
userInfo.AgeGroup = getAgeGroup(birthdate); //You may need to edit getAgeGroup to take in a nullable DateTime
if (result.Any(x => x.Id== userId)) //Any is much faster than count for your check, see Matthew PK's answer.
result.Add(userInfo);
}
答案 2 :(得分:0)
为了加快从数据库中获取数据的速度,您可能需要考虑不同的数据读取方式,而不是通过读取器循环。
DataSet my_dataset = new DataSet();
NpgsqlDataAdapter my_dataadapter = default(NpgsqlDataAdapter);
NpgsqlCommand cmd = new NpgsqlCommand(query, conn);
my_dataadapter = new NpgsqlDataAdapter(cmd);
my_dataadapter.Fill(my_dataset, "mydataset");
然后对数据集做任何事情。
你可能对速度的差异感到非常惊讶。