簡體   English   中英

如何針對大型數據集優化比較性能?

[英]How to optimize the performance of comparisons for large data-sets?

我有一個非常慢的方法,我不確定如何優化它。 我也不太了解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循環之外的數據進行規范化。 我還嘗試使用字典來提高比較速度。 我現在不能做的是規范化數據庫並制作單個語句,而不是檢索整個表。 謝謝大家的幫助。 請告訴我代碼改進是否還有余地。

新代碼:

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

第一個危險信號是您僅使用數據庫保存數據 如果允許的話 ,那頭野獸可以比您更快地搜索。

對於excel中的每一行,為數據庫建立一個相應的搜索語句並啟動它。 讓您的數據庫擔心搜索10K記錄的最佳方法。

第二個危險信號是未對現有數據進行規范化。 您想比較兩條街道,但是必須一遍又一遍地標准化它們。 為什么沒有一個稱為“ NormalizedStreet”的數據庫字段在插入時已經應用了這些方法,而您可以在其中發起“相等”比較以規范化輸入數據?

總結一下:取消循環中的循環。 您剛剛重新創建了數據庫。 對於您的excel的每一行,構建一個(或兩個)語句以查找數據庫中是否存在該語句。 如果您想變得狡猾,請並行運行它們,但是我懷疑只需要700條輸入記錄就可以了。

首先,nvoight是正確的:您應該規范化數據庫中的數據並利用其功能進行搜索。 但是,如果您不能更改數據庫,則可以對代碼進行改進。

1)最重要的是要擺脫循環,只能完成一次。 這是數據規范化(替換,降低等)。 一次遍歷所有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)您可以使用字典或HashSets加快字符串搜索和比較的速度,而不是循環。

3)調用“ _crm”有多快? 也許外部通話會花費一些時間,這也是您運行緩慢的原因。

_crm.CheckContactExists(...)

如果這是sql server,則應該使用ssis。
它具有模糊匹配,這對於匹配來自兩個不同來源的地址上的記錄幾乎是必須的。
我也將使用ssis將數據導入到表中,並對管道中的數據進行任何預處理。
然后可以使用作業來運行整個過程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM