Каков наилучший способ удалить дубликаты из таблицы данных?

Я проверил весь сайт и погуглил в сети, но не смог найти простого решения этой проблемы.

У меня есть таблица данных, в которой около 20 столбцов и 10 тыс. строк. Мне нужно удалить повторяющиеся строки в этой таблице данных на основе 4 ключевых столбцов. Разве в .Net нет функции, которая делает это? Функция, наиболее близкая к тому, что я ищу, была datatable.DefaultView.ToTable(true, массив столбцов для отображения), но эта функция различается по всем столбцам.

Было бы здорово, если бы кто-нибудь помог мне с этим.

РЕДАКТИРОВАТЬ: Мне жаль, что я не понял этого. Эта таблица данных создается путем чтения файла CSV, а не из БД. Поэтому использование SQL-запроса не вариант.


person Khaja Minhajuddin    schedule 04.12.2008    source источник
comment
Можете ли вы использовать для этого базу данных? Или это разовая сделка? Если вы можете использовать базу данных, вы можете сделать это с помощью некоторых умных выборов/представлений.   -  person Mischa Kroon    schedule 04.12.2008


Ответы (12)


Вы можете использовать Linq для наборов данных. Проверьте это. Что-то вроде этого:

// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

List<DataRow> rows = new List<DataRow>();

DataTable contact = ds.Tables["Contact"];

// Get 100 rows from the Contact table.
IEnumerable<DataRow> query = (from c in contact.AsEnumerable()
                              select c).Take(100);

DataTable contactsTableWith100Rows = query.CopyToDataTable();

// Add 100 rows to the list.
foreach (DataRow row in contactsTableWith100Rows.Rows)
    rows.Add(row);

// Create duplicate rows by adding the same 100 rows to the list.
foreach (DataRow row in contactsTableWith100Rows.Rows)
    rows.Add(row);

DataTable table =
    System.Data.DataTableExtensions.CopyToDataTable<DataRow>(rows);

// Find the unique contacts in the table.
IEnumerable<DataRow> uniqueContacts =
    table.AsEnumerable().Distinct(DataRowComparer.Default);

Console.WriteLine("Unique contacts:");
foreach (DataRow uniqueContact in uniqueContacts)
{
    Console.WriteLine(uniqueContact.Field<Int32>("ContactID"));
}
person Eduardo Campañó    schedule 04.12.2008

Как удалить повторяющиеся строки?. (Настройте запрос, чтобы присоединиться к 4 ключевым столбцам)

РЕДАКТИРОВАТЬ: с вашей новой информацией, я считаю, что самым простым способом было бы реализовать IEqualityComparer‹T› и использовать Distinct в ваших строках данных. В противном случае, если вы работаете с IEnumerable/IList вместо DataTable/DataRow, это, безусловно, возможно с некоторым кунг-фу LINQ-to-objects.

РЕДАКТИРОВАТЬ: пример IEqualityComparer

public class MyRowComparer : IEqualityComparer<DataRow>
{

    public bool Equals(DataRow x, DataRow y)
    {
        return (x.Field<int>("ID") == y.Field<int>("ID")) &&
            string.Compare(x.Field<string>("Name"), y.Field<string>("Name"), true) == 0 &&
          ... // extend this to include all your 4 keys...
    }

    public int GetHashCode(DataRow obj)
    {
        return obj.Field<int>("ID").GetHashCode() ^ obj.Field<string>("Name").GetHashCode() etc.
    }
}

Вы можете использовать его следующим образом:

var uniqueRows = myTable.AsEnumerable().Distinct(MyRowComparer);
person liggett78    schedule 04.12.2008

Если у вас есть доступ к Linq, я думаю, вы сможете использовать встроенные групповые функции в коллекции памяти и выбирать повторяющиеся строки.

Найдите в Google Linq Group, например,

person terjetyl    schedule 04.12.2008
comment
это не будет похоже на накладные расходы??? когда то же самое можно сделать простым запросом? Без обид, но я хотел бы узнать его рекламу по одному запросу? - person Samiksha; 04.12.2008

Следует учитывать, что для завершения удаления необходимо вызвать Table.AcceptChanges(). В противном случае удаленная строка по-прежнему присутствует в DataTable, а RowState имеет значение Deleted. И Table.Rows.Count не меняется после удаления.

person Alexey    schedule 01.10.2011

Я думаю, что это лучший способ удалить дубликаты из Datatable с помощью Linq и moreLinq кода:

Линк

RemoveDuplicatesRecords(yourDataTable);


private DataTable RemoveDuplicatesRecords(DataTable dt)
{
    var UniqueRows = dt.AsEnumerable().Distinct(DataRowComparer.Default);
    DataTable dt2 = UniqueRows.CopyToDataTable();
    return dt2;
}

ПодробнееLinq

// Distinctby  column name ID 
var valueDistinctByIdColumn = yourTable.AsEnumerable().DistinctBy(row => new { Id = row["Id"] });
DataTable dtDistinctByIdColumn = valueDistinctByIdColumn.CopyToDataTable();
 

Примечание: moreLinq необходимо добавить библиотеку.

В morelinq вы можете использовать функцию DistinctBy, в которой вы можете указать свойство, по которому вы хотите найти особые объекты.

person Satinder singh    schedule 09.05.2013

Ответ Liggett78 намного лучше - особенно. так как у меня была ошибка! Коррекция следующая...

DELETE TableWithDuplicates
    FROM TableWithDuplicates
        LEFT OUTER JOIN (
            SELECT PK_ID = Min(PK_ID), --Decide your method for deciding which rows to keep
                KeyColumn1,
                KeyColumn2,
                KeyColumn3,
                KeyColumn4
                FROM TableWithDuplicates
                GROUP BY KeyColumn1,
                    KeyColumn2,
                    KeyColumn3,
                    KeyColumn4
            ) AS RowsToKeep
            ON TableWithDuplicates.PK_ID = RowsToKeep.PK_ID
    WHERE RowsToKeep.PK_ID IS NULL
person Community    schedule 04.12.2008


«Эта таблица данных создается путем чтения CSV-файла, а не из БД».

Поэтому установите уникальное ограничение для четырех столбцов в базе данных, и вставки, которые являются дубликатами в соответствии с вашим дизайном, не будут входить. Если только он не решит потерпеть неудачу вместо продолжения, когда это произойдет, но это, безусловно, настраивается в вашем скрипте импорта CSV.

person JeeBee    schedule 04.12.2008
comment
Я не вставляю данные в базу данных. Эти данные записываются в файл csv. - person Khaja Minhajuddin; 04.12.2008

Используйте запрос вместо функций:

DELETE FROM table1 AS tb1 INNER JOIN 
(SELECT id, COUNT(id) AS cntr FROM table1 GROUP BY id) AS tb2
ON tb1.id = tb2.id WHERE tb2.cntr > 1
person Samiksha    schedule 04.12.2008

Это очень простой код, который не требует ни linq, ни отдельных столбцов для фильтрации. Если все значения столбцов в строке равны нулю, они будут удалены.


    public DataSet duplicateRemoval(DataSet dSet) 
{
    bool flag;
    int ccount = dSet.Tables[0].Columns.Count;
    string[] colst = new string[ccount];
    int p = 0;

    DataSet dsTemp = new DataSet();
    DataTable Tables = new DataTable();
    dsTemp.Tables.Add(Tables);

    for (int i = 0; i < ccount; i++)
    {
        dsTemp.Tables[0].Columns.Add(dSet.Tables[0].Columns[i].ColumnName, System.Type.GetType("System.String"));
    }

    foreach (System.Data.DataRow row in dSet.Tables[0].Rows)
    {
        flag = false;
        p = 0;
        foreach (System.Data.DataColumn col in dSet.Tables[0].Columns)
        {
            colst[p++] = row[col].ToString();
            if (!string.IsNullOrEmpty(row[col].ToString()))
            {  //Display only if any of the data is present in column
                flag = true;
            }
        }
        if (flag == true)
        {
            DataRow myRow = dsTemp.Tables[0].NewRow();
            //Response.Write("<tr style=\"background:#d2d2d2;\">");
            for (int kk = 0; kk < ccount; kk++)
            {
                myRow[kk] = colst[kk];         

                // Response.Write("<td class=\"table-line\" bgcolor=\"#D2D2D2\">" + colst[kk] + "</td>");
            }
            dsTemp.Tables[0].Rows.Add(myRow);
        }
    } return dsTemp;
}

Это можно даже использовать для удаления нулевых данных с листа Excel.

person Srikanth V M    schedule 27.05.2010

Попробуй это

Предположим, что dtInput — это ваша таблица данных с повторяющимися записями.

У меня есть новый DataTable dtFinal, в котором я хочу отфильтровать повторяющиеся строки.

Итак, мой код будет примерно таким, как показано ниже.

DataTable dtFinal = dtInput.DefaultView.ToTable(true, 
                           new string[ColumnCount] {"Col1Name","Col2Name","Col3Name",...,"ColnName"});
person Suhas Patil    schedule 16.12.2011

Я не хотел использовать решение Linq выше, поэтому я написал это:

/// <summary>
/// Takes a datatable and a column index, and returns a datatable without duplicates
/// </summary>
/// <param name="dt">The datatable containing duplicate records</param>
/// <param name="ComparisonFieldIndex">The column index containing duplicates</param>
/// <returns>A datatable object without duplicated records</returns>
public DataTable duplicateRemoval(DataTable dt, int ComparisonFieldIndex)
{
    try
    {
        //Build the new datatable that will be returned
        DataTable dtReturn = new DataTable();
        for (int i = 0; i < dt.Columns.Count; i++)
        {
            dtReturn.Columns.Add(dt.Columns[i].ColumnName, System.Type.GetType("System.String"));
        }

        //Loop through each record in the datatable we have been passed
        foreach (DataRow dr in dt.Rows)
        {
            bool Found = false;
            //Loop through each record already present in the datatable being returned
            foreach (DataRow dr2 in dtReturn.Rows)
            {
                bool Identical = true;
                //Compare the column specified to see if it matches an existing record
                if (!(dr2[ComparisonFieldIndex].ToString() == dr[ComparisonFieldIndex].ToString()))
                {
                    Identical = false;
                }
                //If the record found identically matches one we already have, don't add it again
                if (Identical)
                {
                    Found = true;
                    break;
                }
            }
            //If we didn't find a matching record, we'll add this one
            if (!Found)
            {
                DataRow drAdd = dtReturn.NewRow();
                for (int i = 0; i < dtReturn.Columns.Count; i++)
                {
                    drAdd[i] = dr[i];
                }

                dtReturn.Rows.Add(drAdd);
            }
        }
        return dtReturn;
    }
    catch (Exception)
    {
        //Return the original datatable if something failed above
        return dt;
    }
}

Кроме того, это работает со ВСЕМИ столбцами, а не с конкретным индексом столбца:

/// <summary>
/// Takes a datatable and returns a datatable without duplicates
/// </summary>
/// <param name="dt">The datatable containing duplicate records</param>
/// <returns>A datatable object without duplicated records</returns>
public DataTable duplicateRemoval(DataTable dt)
{
    try
    {
        //Build the new datatable that will be returned
        DataTable dtReturn = new DataTable();
        for (int i = 0; i < dt.Columns.Count; i++)
        {
            dtReturn.Columns.Add(dt.Columns[i].ColumnName, System.Type.GetType("System.String"));
        }

        //Loop through each record in the datatable we have been passed
        foreach (DataRow dr in dt.Rows)
        {
            bool Found = false;
            //Loop through each record already present in the datatable being returned
            foreach (DataRow dr2 in dtReturn.Rows)
            {
                bool Identical = true;
                //Compare all columns to see if they match the existing record
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    if (!(dr2[i].ToString() == dr[i].ToString()))
                    {
                        Identical = false;
                    }
                }
                //If the record found identically matches one we already have, don't add it again
                if (Identical)
                {
                    Found = true;
                    break;
                }
            }
            //If we didn't find a matching record, we'll add this one
            if (!Found)
            {
                DataRow drAdd = dtReturn.NewRow();
                for (int i = 0; i < dtReturn.Columns.Count; i++)
                {
                    drAdd[i] = dr[i];
                }

                dtReturn.Rows.Add(drAdd);
            }
        }
        return dtReturn;
    }
    catch (Exception)
    {
        //Return the original datatable if something failed above
        return dt;
    }
}
person Dave Lucre    schedule 03.04.2013