如何优化大型数据集的比较性能?

时间:2018-04-13 07:47:59

标签: c# performance comparison

我有一个非常慢的方法,我不知道如何优化它。我也不知道LINQ是如何工作的,所以如果解决方案是使用LINQ,请解释。非常感谢。

方法参数中的DataTable dtExcel包含数据的第一部分,另一部分来自数据库的_dt。 贯穿两个for循环的数据大致如下:700(dtExcel)* 10,000(_dt)= 7,000,000个比较。

这里是代码:

public async Task<DataTable> GetAdressesFromDB(DataTable dtExcel)
{
    try
    {
        return await Task.Run(() =>
        {
            CurrentProgress = 0;
            ProgressbarDBVisible = true;

            _dtFoundDuplicates.Clear();

            _dt = new DataTable();

            _dt = DBConn.GetAllAddresses(dtExcel);

            ProgressMaximum = dtExcel.Rows.Count;

            for (int i = 0; i < dtExcel.Rows.Count; i++)
            {
                CurrentProgress++;
                for (int y = 0; y < _dt.Rows.Count; y++)
                {
                    // Criteria to check duplicates
                    string compareAdressExcel = "";
                    string compareAdressDB = "";

                    // Get the setted filter criteria and create both excel and db compare strings
                    string[] criteriaFields = ConfigurationManager.AppSettings["strFilter"].Split(',');
                    foreach (String cField in criteriaFields)
                    {
                        string cFieldTrimmed = cField.Trim();
                        if (cFieldTrimmed == "Strasse")
                        {
                            compareAdressExcel += dtExcel.Rows[i][cFieldTrimmed].ToString()
                                                 .ToLower()
                                                 .Replace(" ", "")
                                                 .Replace("str", "strasse")
                                                 .Replace("straße", "strasse")
                                                 .Replace("str.", "strasse");
                            compareAdressDB += _dt.Rows[y][cFieldTrimmed].ToString()
                                               .Replace(" ", "")
                                               .ToLower()
                                               .Replace("str", "strasse")
                                               .Replace("straße", "strasse")
                                               .Replace("str.", "strasse");
                        }
                        else
                        {
                            compareAdressExcel += dtExcel.Rows[i][cFieldTrimmed].ToString().Replace(" ", "").ToLower();
                            compareAdressDB += _dt.Rows[y][cFieldTrimmed].ToString().Replace(" ", "").ToLower();
                        }
                    }

                    // If the company doesn't exists in Database, the contact to that company found in excel
                    // automatically won't exist either. Otherwise, check if contact exists.
                    if (compareAdressExcel == compareAdressDB)
                    {
                        string strOneExistTwoNot = "2";

                        if (!string.IsNullOrWhiteSpace(dtExcel.Rows[i]["FirstName"].ToString().Trim()) && 
                            !string.IsNullOrWhiteSpace(dtExcel.Rows[i]["LastName"].ToString().Trim()))
                        {
                            strOneExistTwoNot = _crm.CheckContactExists(Convert.ToInt32(_dt.Rows[y]["AdressNummer"].ToString().Trim()),
                                                dtExcel.Rows[i]["FirstName"].ToString().Trim(), 
                                                dtExcel.Rows[i]["LastName"].ToString().Trim());
                        }

                        // Check if CheckContactExsists was successful
                        if (strOneExistTwoNot != "1" && strOneExistTwoNot != "2")
                        {
                            throw new Exception(strOneExistTwoNot);
                        }

                        // If Contact exists, mark row and add duplicate row,
                        // otherwise only add duplicate row
                        if (strOneExistTwoNot == "1")
                        {
                            dtExcel.Rows[i]["ContactExists"] = 1;
                            _dtFoundDuplicates.Rows.Add(dtExcel.Rows[i]["ID"], _dt.Rows[y]["AdressNummer"], "1");
                        }
                        else
                        {
                            _dtFoundDuplicates.Rows.Add(dtExcel.Rows[i]["ID"], _dt.Rows[y]["AdressNummer"], "0");
                        }
                        dtExcel.Rows[i]["AdressExists"] = 1;
                    }
                }
            }
            ProgressbarDBVisible = false;
            return dtExcel;
        });
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

修改

好的,所以在@dlxeon的回答的帮助下,我试图将我的数据标准化为我的两个for循环之外的数据。我还尝试使用Dictonary来提高比较速度。我现在不能做的是规范化数据库并制作单个语句而不是检索整个表。谢谢大家的帮助。请告诉我代码是否还有改进空间。

新代码:

public async Task<DataTable> GetAdressesFromDB(DataTable dtExcel)
        {
            try
            {
                return await Task.Run(() =>
                {
                    CurrentProgress = 0;
                    ProgressbarDBVisible = true;

                    _dtFoundDuplicates.Clear();

                    _dt = DBConn.GetAllAddresses();

                    ProgressMaximum = dtExcel.Rows.Count;

                    // Normalization
                    string[] criteriaFields = ConfigurationManager.AppSettings["strFilter"].Split(',').Select(x => x.Trim()).ToArray();

                    Dictionary<int, string> excelAddresses = new Dictionary<int, string>();
                    for (int i = 0; i < dtExcel.Rows.Count; i++)
                    {
                        StringBuilder compareAdressExcel = new StringBuilder();
                        foreach (String cFieldTrimmed in criteriaFields)
                        {
                            if (cFieldTrimmed == "Strasse")
                            {
                                var normalizedValue = dtExcel.Rows[i][cFieldTrimmed].ToString()
                                    .ToLower()
                                    .Replace(" ", "")
                                    .Replace("str", "strasse")
                                    .Replace("straße", "strasse")
                                    .Replace("str.", "strasse");
                                compareAdressExcel.Append(normalizedValue);
                            }
                            else
                            {
                                compareAdressExcel.Append(dtExcel.Rows[i][cFieldTrimmed].ToString().Replace(" ", "").ToLower());
                            }
                        }
                        excelAddresses.Add(i, compareAdressExcel.ToString());
                    }
                    Dictionary<int, string> dbAddresses = new Dictionary<int, string>();
                    for (int i = 0; i < _dt.Rows.Count; i++)
                    {
                        StringBuilder compareAdressDB = new StringBuilder();
                        foreach (String cFieldTrimmed in criteriaFields)
                        {
                            if (cFieldTrimmed == "Strasse")
                            {
                                var normalizedValue = _dt.Rows[i][cFieldTrimmed].ToString()
                                    .ToLower()
                                    .Replace(" ", "")
                                    .Replace("str", "strasse")
                                    .Replace("straße", "strasse")
                                    .Replace("str.", "strasse");
                                compareAdressDB.Append(normalizedValue);
                            }
                            else
                            {
                                compareAdressDB.Append(_dt.Rows[i][cFieldTrimmed].ToString().Replace(" ", "").ToLower());
                            }
                        }
                        dbAddresses.Add(i, compareAdressDB.ToString());
                    }

                    foreach (var exAdd in excelAddresses)
                    {
                        CurrentProgress++;

                        foreach (var dbAdd in dbAddresses)
                        {
                            // If the company doesn't exists in Database, the contact to that company found in excel
                            // automatically won't exist either. Otherwise, check if contact exists.
                            if (exAdd.Value == dbAdd.Value)
                            {
                                string strOneExistTwoNot = "2";

                                if (!string.IsNullOrWhiteSpace(dtExcel.Rows[exAdd.Key]["FirstName"].ToString().Trim()) && 
                                                               !string.IsNullOrWhiteSpace(dtExcel.Rows[exAdd.Key]["LastName"].ToString().Trim()))
                                {
                                    strOneExistTwoNot = _crm.CheckContactExists(Convert.ToInt32(_dt.Rows[dbAdd.Key]["AdressNummer"].ToString().Trim()), 
                                                                                dtExcel.Rows[exAdd.Key]["FirstName"].ToString().Trim(), 
                                                                                dtExcel.Rows[exAdd.Key]["LastName"].ToString().Trim());
                                }

                                // Check if CheckContactExsists was successful
                                if (strOneExistTwoNot != "1" && strOneExistTwoNot != "2")
                                {
                                    throw new Exception(strOneExistTwoNot);
                                }

                                if (strOneExistTwoNot == "1")
                                {
                                    dtExcel.Rows[exAdd.Key]["ContactExists"] = 1;
                                    _dtFoundDuplicates.Rows.Add(dtExcel.Rows[exAdd.Key]["ID"], _dt.Rows[dbAdd.Key]["AdressNummer"], "1");
                                }
                                else
                                {
                                    _dtFoundDuplicates.Rows.Add(dtExcel.Rows[exAdd.Key]["ID"], _dt.Rows[dbAdd.Key]["AdressNummer"], "0");
                                }
                                dtExcel.Rows[exAdd.Key]["AdressExists"] = 1;
                            }
                        }
                    }
                    ProgressbarDBVisible = false;
                    return dtExcel;
                });
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

3 个答案:

答案 0 :(得分:2)

第一个红色标志是您仅将数据库用于保存数据。如果你让它,那野兽可以比你更快地搜索。

对于excel中的每一行,为您的数据库构建一个相应的搜索语句并将其激活。让您的数据库担心搜索10K记录的最佳方式。

第二个红色标志是您的现有数据未进行标准化。你想要比较两条街道,但你必须将它们一遍又一遍地标准化。为什么没有名为“NormalizedStreet”的数据库字段已经 在插入时应用了一次,并且您可以在哪里触发“等于”比较来规范输入数据?

总结一下:废弃你的循环循环。你刚刚彻底改造了数据库。对于excel的每一行,构建一个或多个语句(以确定它是否存在于数据库中)。如果你想变得狡猾,可以并行运行它们,但是我怀疑你需要那些可用的700输入记录。

答案 1 :(得分:1)

首先,nvoight是对的:您应该规范化数据库中的数据并使用其权力进行搜索。但是,如果您无法更改数据库,则可以对代码进行改进。

1)最重要的是摆脱可以做一次的循环。这是数据规范化(替换,tolower等)。迭代所有Excel数据和数据库数据一次,以构建可以直接比较的数据,然后再对您的两个内部循环进行实际比较。您在循环中也不会更改您的配置,因此也可以将其移除。避免额外的字符串分配。您可以使用StringBuilder来构建字符串,而不是使用+ =

类似Excel的东西(然后是Db的类似循环)

string[] criteriaFields = ConfigurationManager.AppSettings["strFilter"].Split(',').Select(x => x.Trim()).ToArray();
List<string> excelAddresses = new List<string>();
for (int i = 0; i < dtExcel.Rows.Count; i++)
{
    StringBuilder compareAdressExcel = new StringBuilder();
    foreach (String cFieldTrimmed in criteriaFields)
    {
        if (cFieldTrimmed == "Strasse")
        {
            var normalizedValue = dtExcel.Rows[i][cFieldTrimmed].ToString()
                .ToLower()
                .Replace(" ", "")
                .Replace("str", "strasse")
                .Replace("straße", "strasse")
                .Replace("str.", "strasse");
            compareAdressExcel.Append(normalizedValue);
        }
        else
        {
            compareAdressExcel.Append(dtExcel.Rows[i][cFieldTrimmed].ToString().Replace(" ", "").ToLower());
        }
    }
    excelAddresses.Add(compareAdressExcel.ToString());
}

然后你可以在主循环中使用标准化值

for (int i = 0; i < dtExcel.Rows.Count; i++)
{
    CurrentProgress++;
    for (int y = 0; y < _dt.Rows.Count; y++)
    {
        // Criteria to check duplicates
        string compareAdressExcel = excelAddresses[i];
        string compareAdressDB = dbAddresses[y];

2)您可以使用Dictionaries或HashSet来加速字符串搜索和比较,而不是循环。

3)对&#34; _crm&#34;的呼叫有多快?也许外部呼叫需要一段时间,这也是你速度缓慢的原因。

_crm.CheckContactExists(...)

答案 2 :(得分:0)

如果这是sql server,那么你应该使用ssis 它具有模糊匹配,这对于匹配来自两个不同来源的地址上的记录几乎是必须的 我也会使用ssis将数据导入表中,并对管道中的数据进行任何预处理 那么整个事情可以用一份工作来运行。