データテーブルから重複を削除する最良の方法は何ですか?
-
19-08-2019 - |
質問
サイト全体を確認し、ネット上でグーグル検索しましたが、この問題の簡単な解決策を見つけることができませんでした。
約20列と10K行のデータテーブルがあります。 4つのキー列に基づいて、このデータテーブル内の重複行を削除する必要があります。 .Netにはこれを行う機能はありませんか?私が探しているものに最も近い関数はdatatable.DefaultView.ToTable(true、表示する列の配列)でしたが、この関数はすべて列で明確に区別します。
誰かがこれを手伝ってくれるといいなと思います。
編集:これについてはっきりしないのは残念です。このデータテーブルは、DBからではなくCSVファイルを読み取ることで作成されています。したがって、SQLクエリを使用することはオプションではありません。
解決
Linq to Datasetsを使用できます。 こちらを確認してください。このようなもの:
// 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"));
}
他のヒント
重複した行を削除するにはどうすればよいですか。 (クエリを調整して、4つのキー列で結合します)
編集:新しい情報を使用して、IEqualityComparer <!> lt; T <!> gt;を実装するのが最も簡単な方法だと思います。データ行でDistinctを使用します。それ以外の場合、DataTable / DataRowの代わりにIEnumerable / IListを使用している場合、一部のLINQ-to-objects kung-fuで確かに可能です。
編集: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);
Linqにアクセスできる場合は、メモリ内コレクションでビルトイングループ機能を使用して、重複する行を選択できるはずです
例としてGoogleでLinq Groupを検索
削除を完了するには、Table.AcceptChanges()を呼び出す必要があることを考慮する必要があります。それ以外の場合、削除された行は、RowStateがDeletedに設定された状態でDataTableに残ります。また、Table.Rows.Countは削除後に変更されません。
これは、Linq
とmoreLinq
を使用して Datatable から重複を削除する最良の方法である必要があると思います
コード:
Linq
RemoveDuplicatesRecords(yourDataTable);
private DataTable RemoveDuplicatesRecords(DataTable dt)
{
var UniqueRows = dt.AsEnumerable().Distinct(DataRowComparer.Default);
DataTable dt2 = UniqueRows.CopyToDataTable();
return dt2;
}
ブログ記事:重複行レコードを削除DataTable Asp.net c#
MoreLinq
// Distinctby column name ID
var valueDistinctByIdColumn = yourTable.AsEnumerable().DistinctBy(row => new { Id = row["Id"] });
DataTable dtDistinctByIdColumn = valueDistinctByIdColumn.CopyToDataTable();
注:<=>ライブラリを追加する必要があります。
morelinq では、DistinctByという関数を使用できます。この関数では、DistinctByオブジェクトを検索するプロパティを指定できます。
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
JET 4.0 OLE DBプロバイダーは、 System.Data.OleDb名前空間は、コンマ区切りのテキストファイルにアクセスします( DataSet / DataTable)。
または、Microsoft Text Driver for ODBCを使用して、 ODBCドライバーを使用してファイルにアクセスするためのSystem.Data.Odbc名前空間。
これにより、他の人が提案したように、SQLクエリ経由でデータにアクセスできます。
<!> quot;このデータテーブルは、DBからではなくCSVファイルを読み取ることで作成されています。<!> quot;
データベースの4つの列に一意の制約を設定すると、デザインの下に重複する挿入は挿入されません。これが発生したときに続行する代わりに失敗することを決定しない限り、これは確実にCSVで構成可能ですインポートスクリプト。
関数の代わりにクエリを使用します:
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
これは、フィルターを実行するためにlinqも個々の列も必要としない非常に単純なコードです。 行の列の値がすべてnullの場合、削除されます。
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シートからnullデータを削除するためにも使用できます。
これを試してください
dtInputが重複レコードを持つデータテーブルであると考えてみましょう。
重複する行をフィルタリングする新しいDataTable dtFinalがあります。
したがって、私のコードは以下のようになります。
DataTable dtFinal = dtInput.DefaultView.ToTable(true,
new string[ColumnCount] {"Col1Name","Col2Name","Col3Name",...,"ColnName"});
上記の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;
}
}