Question

J'ai consulté l'ensemble du site et cherché sur Internet, mais je n'ai pas trouvé de solution simple à ce problème.

J'ai un datatable qui a environ 20 colonnes et 10K lignes. J'ai besoin de supprimer les lignes en double dans ce datatable basé sur 4 colonnes de clé. Est-ce que .Net n'a pas une fonction qui fait ça? La fonction la plus proche de ce que je cherchais était datatable.DefaultView.ToTable (true, tableau de colonnes à afficher), mais cette fonction effectue une distinction sur toutes les colonnes.

Ce serait formidable si quelqu'un pouvait m'aider avec cela.

EDIT: Je suis désolé de ne pas être clair sur ce point. Ce datatable est créé en lisant un fichier CSV et non à partir d'une base de données. Donc, utiliser une requête SQL n'est pas une option.

Était-ce utile?

La solution

Vous pouvez utiliser Linq to Datasets. Vérifiez ceci . Quelque chose comme ça:

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

Autres conseils

Comment puis-je supprimer les lignes en double? . (Ajustez la requête à joindre sur vos 4 colonnes clés)

EDIT: avec vos nouvelles informations, je pense que le moyen le plus simple serait de mettre en oeuvre IEqualityComparer < T > et utilisez Distinct sur vos lignes de données. Sinon, si vous travaillez avec IEnumerable / IList au lieu de DataTable / DataRow, cela est certainement possible avec certains kung-fu LINQ-to-objects.

EDIT: exemple 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.
    }
}

Vous pouvez l'utiliser comme ceci:

var uniqueRows = myTable.AsEnumerable().Distinct(MyRowComparer);

Si vous avez accès à Linq, je pense que vous devriez pouvoir utiliser la fonctionnalité de groupe intégrée dans la collection en mémoire et choisir les lignes en double.

Recherchez des groupes Linq dans Google à l'aide d'exemples

.

Il convient de noter que Table.AcceptChanges () doit être appelé pour terminer la suppression. Sinon, la ligne supprimée est toujours présente dans DataTable avec RowState défini sur Supprimé. Et Table.Rows.Count n'est pas modifié après la suppression.

Je pense que ce doit être le meilleur moyen de supprimer les doublons de Datatable en utilisant Linq et moreLinq Code:

Linq

RemoveDuplicatesRecords(yourDataTable);


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

Article de blog: Supprimer les enregistrements de lignes en double de DataTable Asp.net c #

MoreLinq

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

Remarque: <=> besoin d'ajouter une bibliothèque.

Dans morelinq , vous pouvez utiliser la fonction DistinctBy dans laquelle vous pouvez spécifier la propriété sur laquelle vous souhaitez rechercher des objets Distinct.

Article de blog: utiliser moreLinq DistinctBy méthode pour supprimer les enregistrements en double

La réponse de Liggett78 est bien meilleure - esp. comme le mien avait une erreur! Correction comme suit ...

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

Trouvé ceci sur bytes.com :

  

Vous pouvez utiliser le fournisseur JET 4.0 OLE DB avec les classes du   System.Data.OleDb pour accéder au fichier texte délimité par des virgules (à l'aide d'un   DataSet / DataTable).

     

Vous pouvez également utiliser Microsoft Text Driver pour ODBC avec les classes du   Espace de noms System.Data.Odbc pour accéder au fichier à l'aide de pilotes ODBC.

Cela vous permettrait d'accéder à vos données via des requêtes SQL, comme d'autres l'ont proposé.

& "; Ce datatable est créé en lisant un fichier CSV et non à partir d'une base de données. &";

Mettez donc une contrainte unique sur les quatre colonnes de la base de données. Les insertions dupliquées dans votre conception n'entreront pas. Sauf si cela décide d'échouer au lieu de continuer lorsque cela se produit, cela est sûrement configurable dans votre CSV. script d'importation.

Utilisez une requête au lieu de fonctions:

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

Il s'agit d'un code très simple qui ne nécessite ni linq ni des colonnes individuelles pour effectuer le filtre. Si toutes les valeurs des colonnes d'une ligne sont nulles, elles seront supprimées.

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

Ceci peut même être utilisé pour supprimer des données nulles de la feuille Excel.

Essayez ceci

Considérons que dtInput est votre table de données avec des enregistrements en double.

J'ai un nouveau DataTable dtFinal dans lequel je veux filtrer les lignes en double.

Donc, mon code sera comme ci-dessous.

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

Je n'aimais pas utiliser la solution Linq ci-dessus, alors j'ai écrit ceci:

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

De plus, cela fonctionne sur TOUTES les colonnes plutôt que sur un index de colonne spécifique:

/// <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;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top