【问题标题】:What is the best way to remove duplicates from a datatable?从数据表中删除重复项的最佳方法是什么?
【发布时间】:2008-12-04 11:05:00
【问题描述】:

我已经检查了整个网站并在网上搜索过,但无法找到解决此问题的简单方法。

我有一个大约有 20 列和 10K 行的数据表。我需要根据 4 个关键列删除此数据表中的重复行。 .Net 没有这样做的功能吗?最接近我正在寻找的函数是 datatable.DefaultView.ToTable(true, array of columns to display),但是这个函数在 all 列上是不同的。

如果有人可以帮助我,那就太好了。

编辑:我很抱歉不清楚这一点。此数据表是通过读取 CSV 文件而不是从数据库中创建的。所以不能使用 SQL 查询。

【问题讨论】:

  • 你可以为你使用数据库吗?还是这是一次性交易?如果你可以使用数据库,你可以通过一些智能选择/视图来做到这一点。

标签: c# datatable duplicates


【解决方案1】:

您可以使用 Linq to Datasets。检查this。像这样的:

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

【讨论】:

    【解决方案2】:

    How can I remove duplicate rows?。 (调整那里的查询以加入您的 4 个关键列)

    编辑:根据您的新信息,我相信最简单的方法是实现 IEqualityComparer 并在您的数据行上使用 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);
    

    【讨论】:

      【解决方案3】:

      如果您可以访问 Linq,我认为您应该能够使用内存集合中的内置组功能并挑选出重复的行

      在 Google 中搜索 Linq Group 以获取示例

      【讨论】:

      • 这会不会有点过头了???什么时候可以通过一个简单的查询来完成同样的事情?无意冒犯,但我想通过单个查询了解它的广告?
      【解决方案4】:

      需要注意的是,必须调用 Table.AcceptChanges() 才能完成删除。否则,删除的行仍然存在于 DataTable 中,并且 RowState 设置为 Deleted。并且 Table.Rows.Count 删除后不会改变。

      【讨论】:

        【解决方案5】:

        我认为这一定是使用 LinqmoreLinqDatatable 中删除重复项的最佳方法 代码:

        Linq

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

        MoreLinq

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

        注意:moreLinq需要添加库。

        morelinq 中,您可以使用名为 DistinctBy 的函数,您可以在其中指定要查找 Distinct 对象的属性。

        【讨论】:

          【解决方案6】:

          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
          

          【讨论】:

            【解决方案7】:

            bytes.com找到这个:

            您可以将 JET 4.0 OLE DB 提供程序与 System.Data.OleDb 命名空间以访问逗号分隔的文本文件(使用 数据集/数据表)。

            或者您可以将 Microsoft Text Driver for ODBC 与 System.Data.Odbc 命名空间以使用 ODBC 驱动程序访问文件。

            这将允许您通过 sql 查询访问您的数据,正如其他人所建议的那样。

            【讨论】:

              【解决方案8】:

              “这个数据表是通过读取 CSV 文件而不是从数据库中创建的。”

              因此,对数据库中的四列设置唯一约束,并且在您的设计下重复的插入不会进入。除非它决定失败而不是在发生这种情况时继续,但这肯定可以在您的 CSV 中配置导入脚本。

              【讨论】:

              • 我没有将数据插入数据库。此数据正在写入 csv 文件。
              【解决方案9】:

              使用查询而不是函数:

              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
              

              【讨论】:

                【解决方案10】:

                这是一个非常简单的代码,不需要 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 表中删除空数据。

                【讨论】:

                  【解决方案11】:

                  试试这个

                  让我们认为 dtInput 是您的数据表,其中包含重复记录。

                  我有一个新的 DataTable dtFinal,我想在其中过滤重复的行。

                  所以我的代码如下所示。

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

                  【讨论】:

                    【解决方案12】:

                    我并不热衷于使用上面的 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;
                        }
                    }
                    

                    【讨论】:

                      【解决方案13】:

                      为了完整起见,我附上了一个基于此处已有的一些答案的示例。当其余列可能不同时,此解决方案按 fieldKey1 将表过滤为 N。但也过滤重复中匹配的第一个,其他两列的最低值:

                      return dt.AsEnumerable()
                          .Distinct(DataRowComparer.Default)
                          .GroupBy(r => new
                          {
                              fieldKey1 = r.Field<int>("fieldKey1"), 
                              fieldKey2 = r.Field<string>("fieldKey2"), 
                              fieldKeyn = r.Field<DateTime>("fieldKeyn")
                          })
                          .Select(g =>  
                              g.OrderBy( dr => dr.Field<int>( "OtherField1" ) )
                                  .ThenBy( dr => dr.Field<int>( "OtherField2" ) )
                                      .First())
                          .CopyToDataTable();
                      

                      所以数据表dt:

                      fieldKey1 fieldKey2 fieldKeyn OtherField1 OtherField2 OtherField3
                      1 Two 31-12-2020 4 3 xyz7
                      2 Other 31-12-2021 4 3 xyz100
                      1 Two 31-12-2020 2 2 xyz3
                      1 Two 31-12-2020 2 3 xyz4
                      1 Two 31-12-2020 1 2 xyz1
                      1 Two 31-12-2020 1 4 xyz2
                      1 Two 31-12-2020 3 3 xyz5
                      1 Two 31-12-2020 3 3 xyz6

                      会返回:

                      fieldKey1 fieldKey2 fieldKeyn OtherField1 OtherField2 OtherField3
                      1 Two 31-12-2020 1 2 xyz1
                      2 Other 31-12-2021 4 3 xyz100

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2016-10-24
                        • 1970-01-01
                        • 2020-08-03
                        • 1970-01-01
                        • 2010-09-26
                        • 2019-09-01
                        • 2011-05-23
                        • 2018-02-19
                        相关资源
                        最近更新 更多