1

在程序中,我创建了以下逻辑,用于从数据库中读取数据并将其存储到 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 条记录。

有谁知道如何加快从数据库读取数据的速度?

4

3 回答 3

3

.Count当你真正的意思时,你正在使用.Any()
每当你打电话时.Count,你都在枚举整个集合只是为了看看你是否有一个匹配......

考虑一下您要问的问题:
“您有多少行符合这个条件?那个数字等于零吗?”

您真正的意思是:
“是否有任何行符合此条件?”

在这种情况下,您可以创建 userId 值的 Hashset。检查哈希集(或字典)中的存在比检查列表中的存在要快得多。

此外,如果您已经拥有 userId,那么您会无缘无故地解析并读取所有值。首先检查myHashset.Contains(userId),然后添加。

这是它慢的主要原因。对于 n 行,您正在执行集合的第 n 个三角形枚举!

编辑:考虑这个未经测试的更改:我不知道您的读者是否支持像GetString()这样的类型化读取方法,如果它不简单地使用您以前拥有的方法。

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()完全消除调用。

编辑:我刚刚注意到我的示例从未将 userId 添加到哈希中...哎呀...将其添加到那里。

于 2012-06-11T16:45:01.817 回答
2

所有这些 execption 处理都会大大降低您的程序速度。Exceptions are for Exceptional Cases如果你的代码抛出超过 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]?它已经是日期时间了吗?另一件事是停止在任何地方对对象调用 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);
}
于 2012-06-11T16:34:44.927 回答
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");

然后对数据集做任何事情。
您可能会对速度的差异感到非常惊讶。

于 2014-01-08T22:22:51.490 回答