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);
                }

このコードのパフォーマンスは非常に悪いです。200 万を超えるレコードがあり、30 分後にリスト userInfo には 300.000 レコードしか含まれていません。

データベースからのデータ読み取りを高速化する方法を知っている人はいますか?

4

3 に答える 3

3

あなたが.Count本当に意味するときに使用しているのは、電話を.Any()
かけるときはいつでも.Count、単一の一致があるかどうかを確認するためだけにコレクション全体を列挙しているということです。

あなたが尋ねている質問を考えてみてください:
「この条件に一致する行はいくつありますか?その数はゼロに等しいですか?」

本当の意味は、
「この条件に一致する行はありますか?」です。

そのコンテキストでは、userId値のハッシュセットを作成できます。ハッシュセット(または辞書)に存在するかどうかを確認する方が、リストにあるかどうかを確認するよりもはるかに高速です。

さらに、すでに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

その実行処理のすべてがあなたのプログラムをかなり遅くしています。例外は例外的な場合です。コードが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ですか?もう1つの方法は、あらゆる場所のオブジェクトに対する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 に答える