Domanda

Ho controllato l'intero sito e cercato su Google in rete, ma non sono riuscito a trovare una soluzione semplice a questo problema.

Ho un datatable che ha circa 20 colonne e 10.000 righe.Devo rimuovere le righe duplicate in questo datatable in base a 4 colonne chiave..Net non ha una funzione che fa questo?La funzione più vicina a quello che sto cercando era datatable.DefaultView.ToTable(true, array di colonne da visualizzare), ma questa funzione fa una differenza Tutto le colonne.

Sarebbe fantastico se qualcuno potesse aiutarmi con questo.

MODIFICARE:Mi dispiace di non essere stato chiaro su questo.Questo datatable viene creato leggendo un file CSV e non da un DB.Quindi utilizzare una query SQL non è un'opzione.

È stato utile?

Soluzione

È possibile utilizzare Linq per i set di dati. Controlla questo . Qualcosa del genere:

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

Altri suggerimenti

Come posso rimuovere le righe duplicate? . (Regola la query lì per unirti alle tue 4 colonne chiave)

EDIT: con le tue nuove informazioni credo che il modo più semplice sarebbe implementare IEqualityComparer < T > e usa Distinct nelle tue righe di dati. Altrimenti se stai lavorando con IEnumerable / IList invece di DataTable / DataRow, è certamente possibile con alcuni kung-fu LINQ-to-object.

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

Puoi usarlo in questo modo:

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

Se hai accesso a Linq penso che dovresti essere in grado di utilizzare la funzionalità di gruppo integrata nella raccolta in memoria e selezionare le righe duplicate

Cerca in Google il gruppo Linq per esempi

È necessario tenere presente che è necessario chiamare Table.AcceptChanges() per completare l'eliminazione.In caso contrario, la riga eliminata è ancora presente in DataTable con RowState impostato su Eliminato.E Table.Rows.Count non viene modificato dopo l'eliminazione.

Penso che questo debba essere il modo migliore per rimuovere i duplicati da Datatable usando Linq e moreLinq Codice:

Linq

RemoveDuplicatesRecords(yourDataTable);


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

Articolo del blog: Rimuovi i record di righe duplicate da DataTable Asp.net c #


MoreLinq

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

Nota: <=> è necessario aggiungere la libreria.

In morelinq puoi usare la funzione chiamata DistinctBy in cui puoi specificare la proprietà su cui vuoi trovare gli oggetti Distinct.

Articolo di blog: Utilizzo di moreLinq DistinctBy metodo per rimuovere i record duplicati

La risposta di Liggett78 è molto migliore - esp. come il mio ha avuto un errore! Correzione come segue ...

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

Trovato su bytes.com :

  

È possibile utilizzare il provider OLE DB JET 4.0 con le classi in   Spazio dei nomi System.Data.OleDb per accedere al file di testo delimitato da virgole (usando a   DataSet / DataTable).

     

Oppure puoi usare Microsoft Text Driver per ODBC con le classi in   Spazio dei nomi System.Data.Odbc per accedere al file utilizzando i driver ODBC.

Ciò ti consentirebbe di accedere ai tuoi dati tramite query sql, come altri hanno proposto.

" Questo datatable viene creato leggendo un file CSV e non da un DB. "

Quindi metti un vincolo univoco sulle quattro colonne nel database e gli inserti che sono duplicati sotto il tuo disegno non entreranno. A meno che non decida di fallire invece di continuare quando ciò accade, ma sicuramente questo è configurabile nel tuo CSV script di importazione.

Utilizza una query anziché le funzioni:

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

Questo è un codice molto semplice che non richiede linq né singole colonne per eseguire il filtro. Se tutti i valori delle colonne di una riga sono nulli, verranno eliminati.


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

Questo può anche essere usato per rimuovere i dati null dal foglio Excel.

Prova questo

Consideriamo dtInput come tabella dei dati con record duplicati.

Ho una nuova DataTable dtFinal in cui voglio filtrare le righe duplicate.

Quindi il mio codice sarà simile al seguente.

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

Non volevo usare la soluzione Linq sopra, quindi ho scritto questo:

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

Inoltre, funziona su TUTTE le colonne anziché su un indice di colonna specifico:

/// <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;
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top