DataTable에서 특정 행 삭제


85

DataTable에서 일부 행을 삭제하고 싶지만 다음과 같은 오류가 발생합니다.

컬렉션이 수정되었습니다. 열거 작업이 실행되지 않을 수 있음

이 코드를 삭제하는 데 사용합니다.

foreach(DataRow dr in dtPerson.Rows){
    if(dr["name"].ToString()=="Joe")
        dr.Delete();
}

그렇다면 문제는 무엇이며 어떻게 해결합니까? 어떤 방법을 권합니까?

답변:


169

컬렉션에서 항목을 삭제하면 해당 컬렉션이 변경된 것이므로 계속 열거 할 수 없습니다.

대신 다음과 같은 For 루프를 사용하십시오.

for(int i = dtPerson.Rows.Count-1; i >= 0; i--)
{
    DataRow dr = dtPerson.Rows[i];
    if (dr["name"] == "Joe")
        dr.Delete();
}
dtPerson.AcceptChanges();

현재 인덱스를 삭제 한 후 행을 건너 뛰지 않도록 역순으로 반복합니다.


@Slugster가 나를 이겼습니다! (나는 당신의 변경 [ii]에를 [i]:-) 그러나,
Widor

11
이것은 올바르지 않습니다. foreach를 사용하여 행을 삭제하는 동안 테이블을 반복 있습니다 . Steve의 답변을 참조하십시오 .
Alexander Garden

3
이 답변에는 @bokkie의 답변도 포함되어야합니다. DataTable나중에 사용하면 예외가 발생합니다. 올바른 방법은 호출하는 것입니다 Remove()소스에 DataTable- dtPerson.Rows.Remove(dr).
Code.me

DataTable을 사용하여 데이터베이스 서버의 테이블을 업데이트하는 경우 @Steve가 더 나은 답변을 제공합니다. 단일 루프에서 행을 삭제됨으로 표시하고, 행을 업데이트하고, 새 행을 모두 추가 할 수 있습니다. SqlAdapter를 사용하여 Db 테이블에 변경 사항을 커밋 할 수 있습니다. 문제가 얼마나 자주 발생하는지 고려할 때 전체 프로세스는 생각보다 훨씬 복잡하지만 작동합니다. DataTable의 트랜잭션 특성을 활용하지 않으려면 개체 컬렉션과 namco 또는 Widor 접근 방식을 사용했습니다.
BH

사용하지 않습니다 Delete()에 대한 호출을 필요로 AcceptChanges()적용하려면 삭제하려면?
Broots Waymb 2017 년

128

모든 사람이 ' 열거 형에서 행을 삭제할 수 없습니다 '라는 유행에 뛰어 들기 전에 먼저 DataTables가 트랜잭션 적이라는 것을 인식하고 AcceptChanges () 를 호출 할 때까지 기술적으로 변경 사항을 제거하지 않아야합니다.

Delete 를 호출하는 동안이 예외가 표시되는 경우 이미 변경 보류중인 데이터 상태 입니다. 예를 들어, 데이터베이스에서 방금로드 한 경우, foreach 루프 내에있는 경우 Delete를 호출하면 예외가 발생합니다.

그러나! 그러나!

데이터베이스에서 행을로드하고 ' AcceptChanges () ' 함수를 호출하면 보류중인 모든 변경 사항을 DataTable에 커밋합니다. 이제 Delete ()를 호출하는 행 목록을 순회 할 수 있습니다. 단순히 행을 Deletion 용으로 표시하지만 AcceptChanges ()다시 호출 할 때까지 커밋되지 않기 때문입니다.

나는이 응답이 약간 오래된 것임을 알고 있지만 최근에 비슷한 문제를 다루어야했고 이것이 10 년 된 코드를 작업하는 미래의 개발자에게 약간의 고통을 덜어주기를 바랍니다. :)


추신 다음은 Jeff가 추가 한 간단한 코드 예제입니다 .

씨#

YourDataTable.AcceptChanges(); 
foreach (DataRow row in YourDataTable.Rows) {
    // If this row is offensive then
    row.Delete();
} 
YourDataTable.AcceptChanges();

VB.Net

ds.Tables(0).AcceptChanges()
For Each row In ds.Tables(0).Rows
    ds.Tables(0).Rows(counter).Delete()
    counter += 1
Next
ds.Tables(0).AcceptChanges()

C # 버전의 경우 () 대신 {및}를 사용해야합니다.
BugLover 2014

2
또한 (내가 생각하는) 더 도움이 변경 될 수 있음 object row_loopVariable in ds.Tables(0).RowsDataRow row in ds.Tables(0).Rows
BugLover

2
Ffs, 이것은 악몽 주말 배포 동안 나를 구해주었습니다. 당신은 모든 맥주를 마실 자격이 있습니다!
James Love


좋은 코드. 한 가지, C #에서 1 씩 증가하는 일반적인 방법 counter++counter+= 1.
MQuiggGeorgia

18

이 솔루션으로 :

for(int i = dtPerson.Rows.Count-1; i >= 0; i--) 
{ 
    DataRow dr = dtPerson.Rows[i]; 
    if (dr["name"] == "Joe")
        dr.Delete();
} 

행을 삭제 한 후 데이터 테이블을 사용하려는 경우 오류가 발생합니다. 따라서 할 수있는 일은 다음 dr.Delete();과 같습니다.dtPerson.Rows.Remove(dr);


16

이것은 나를 위해 작동합니다.

List<string> lstRemoveColumns = new List<string>() { "ColValue1", "ColVal2", "ColValue3", "ColValue4" };
List<DataRow> rowsToDelete = new List<DataRow>();

foreach (DataRow row in dt.Rows) {
    if (lstRemoveColumns.Contains(row["ColumnName"].ToString())) {
        rowsToDelete.Add(row);
    }
}

foreach (DataRow row in rowsToDelete) {
    dt.Rows.Remove(row);
}

dt.AcceptChanges();

그래서 dt.AcceptChanges () 그리워하기 쉬운
마태 복음 잠금

"DataRow 클래스의 Delete 메서드를 호출하여 제거 할 행만 표시 할 수도 있습니다. Remove를 호출하는 것은 Delete를 호출 한 다음 AcceptChanges를 호출하는 것과 같습니다. DataRowCollection 개체를 반복하는 동안 foreach 루프에서 Remove를 호출하면 안됩니다. 컬렉션의 상태를 수정합니다. " msdn.microsoft.com/de-de/library/…를 참조 하십시오. 건배합니다.
Andreas Krohn

9
DataRow[] dtr=dtPerson.select("name=Joe");
foreach(var drow in dtr)
{
   drow.delete();
}
dtperson.AcceptChanges();

도움이 되길 바랍니다


1
명령은 메소드가 .net btw에서 대소 문자를 구분 drow.Delete();하지 않습니다drow.delete();
MethodMan

5

DataTable 에서 전체 행 을 제거하려면 다음과 같이하십시오.

DataTable dt = new DataTable();  //User DataTable
DataRow[] rows;
rows = dt.Select("UserName = 'KarthiK'");  //'UserName' is ColumnName
foreach (DataRow row in rows)
     dt.Rows.Remove(row);

4

또는 DataTable Row 컬렉션 을 목록으로 변환 하십시오.

foreach(DataRow dr in dtPerson.Rows.ToList())
{
    if(dr["name"].ToString()=="Joe")
    dr.Delete();
}

1

문제가있는 곳 : foreach 루프 내의 컬렉션에서 항목을 삭제하는 것은 금지되어 있습니다.

솔루션 : Widor가 작성한 것처럼 수행하거나 두 개의 루프를 사용하십시오. DataTable의 첫 번째 단계에서는 삭제하려는 행에 대한 참조 만 임시 목록에 저장합니다. 그런 다음 임시 목록의 두 번째 단계에서 해당 행을 삭제합니다.


1
<asp:GridView ID="grd_item_list" runat="server" AutoGenerateColumns="false" Width="100%" CssClass="table table-bordered table-hover" OnRowCommand="grd_item_list_RowCommand">
    <Columns>
        <asp:TemplateField HeaderText="No">
            <ItemTemplate>
                <%# Container.DataItemIndex + 1 %>
            </ItemTemplate>
        </asp:TemplateField>            
        <asp:TemplateField HeaderText="Actions">
            <ItemTemplate>                    
                <asp:Button ID="remove_itemIndex" OnClientClick="if(confirm('Are You Sure to delete?')==true){ return true;} else{ return false;}" runat="server" class="btn btn-primary" Text="REMOVE" CommandName="REMOVE_ITEM" CommandArgument='<%# Container.DataItemIndex+1 %>' />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

 **This is the row binding event**

protected void grd_item_list_RowCommand(object sender, GridViewCommandEventArgs e) {

    item_list_bind_structure();

    if (ViewState["item_list"] != null)
        dt = (DataTable)ViewState["item_list"];


    if (e.CommandName == "REMOVE_ITEM") {
        var RowNum = Convert.ToInt32(e.CommandArgument.ToString()) - 1;

        DataRow dr = dt.Rows[RowNum];
        dr.Delete();

    }

    grd_item_list.DataSource = dt;
    grd_item_list.DataBind();
}

1

나는 이것이 매우 오래된 질문이라는 것을 알고 있으며 며칠 전에 비슷한 상황이 발생했습니다.

문제는 내 테이블에 약입니다. 10000 개의 행이므로 루핑 트로프 DataTable행은 매우 느 렸습니다.

마지막으로 DataTable원하는 결과, 명확한 소스 DataTablemerge임시 결과 DataTable를 소스 1로 복사하는 훨씬 빠른 솔루션을 찾았습니다 .

참고 : 대신 호출 됨을 검색합니다 Joe. 이름이없는 모든 레코드를 검색해야합니다 (검색 과 약간 반대).DataRownameJoe

예 ( vb.net)가 있습니다.

'Copy all rows into tmpTable whose not contain Joe in name DataRow
Dim tmpTable As DataTable = drPerson.Select("name<>'Joe'").CopyToTable
'Clear source DataTable, in Your case dtPerson
dtPerson.Clear()
'merge tmpTable into dtPerson (rows whose name not contain Joe)
dtPerson.Merge(tmpTable)
tmpTable = Nothing

이 짧은 솔루션이 누군가를 도울 수 있기를 바랍니다.

c#코드 가 있습니다 (온라인 변환기를 사용했기 때문에 정확하지 않습니다 :() :

//Copy all rows into tmpTable whose not contain Joe in name DataRow
DataTable tmpTable = drPerson.Select("name<>'Joe'").CopyToTable;
//Clear source DataTable, in Your case dtPerson
dtPerson.Clear();
//merge tmpTable into dtPerson (rows whose name not contain Joe)
dtPerson.Merge(tmpTable);
tmpTable = null;

물론 Try/Catch결과가없는 경우에 사용 했으므로 (예 : Your dtPersondo n't contain name Joeit will throw exception will throw), You do nothing with your table, 그것은 변경되지 않은 상태로 유지됩니다.


0

내 앱에 데이터 세트가 있고 변경 사항 (행 삭제)을 설정하려고했지만 ds.tabales["TableName"]읽기 전용입니다. 그런 다음이 해결책을 찾았습니다.

WPF C#앱입니다.

try {
    var results = from row in ds.Tables["TableName"].AsEnumerable() where row.Field<string>("Personalid") == "47" select row;                
    foreach (DataRow row in results) {
        ds.Tables["TableName"].Rows.Remove(row);                 
    }           
}

0

데이터 테이블에서 ID 열을 가져오고 제거하기 위해 이것을 시도합니다.

if (dt1.Columns.Contains("ID"))
{
    for (int i = dt1.Rows.Count - 1; i >= 0; i--)
    {
        DataRow dr = dt1.Rows[i];

        if (dr["ID"].ToString() != "" && dr["ID"].ToString() != null)
        {
            dr.Delete();
        }
    }

    dt1.Columns.Remove("ID");
}

0

여기에서 정답의 여러 부분을보고 있지만 모두 모아서 몇 가지를 설명하겠습니다.

우선 AcceptChanges테이블의 전체 트랜잭션을 유효성 검사 및 커밋 된 것으로 표시하는 데만 사용해야합니다. 즉, 예를 들어 SQL 서버에 바인딩하기 위해 DataTable을 데이터 소스로 사용하는 경우 AcceptChanges수동으로 호출 하면 변경 사항 이 SQL 서버에 저장되지 않습니다 .

이 문제를 더 혼란스럽게 만드는 것은 실제로 예외가 발생하는 두 가지 경우가 있으며 둘 다 방지해야한다는 것입니다.

1. IEnumerable의 컬렉션 수정

열거 자의 내부 인덱싱에 영향을 미칠 수 있으므로 열거중인 컬렉션에 인덱스를 추가하거나 제거 할 수 없습니다. 이 문제를 해결하는 방법에는 두 가지가 있습니다. for 루프에서 자체 인덱싱을 수행하거나 열거 형에 대해 별도의 컬렉션 (수정되지 않은)을 사용하는 것입니다.

2. 삭제 된 항목 읽기 시도

DataTable은 트랜잭션 컬렉션이므로 항목을 삭제 표시 할 수 있지만 여전히 열거 형에 나타납니다. 즉, 열에 대해 삭제 된 항목을 요청 "name"하면 예외가 발생합니다. 즉 dr.RowState != DataRowState.Deleted, 열을 쿼리하기 전에 확인해야합니다 .

함께 모아서

우리는 지저분 해지고 모든 작업을 수동으로 수행하거나 DataTable이 모든 작업을 수행하도록하고 다음을 수행하여 명령문을 SQL 호출처럼 보이게 만들 수 있습니다.

string name = "Joe";
foreach(DataRow dr in dtPerson.Select($"name='{name}'"))
    dr.Delete();

DataTable의 Select함수 를 호출 하면 쿼리가 DataTable에서 이미 삭제 된 항목을 자동으로 방지합니다. 그리고이 Select함수는 일치하는 배열을 반환하기 때문에 열거하고있는 컬렉션은를 호출 할 때 수정되지 않습니다 dr.Delete(). 또한 코드를 시끄럽게 만들지 않고 변수를 선택할 수 있도록 문자열 보간을 사용하여 Select 표현식을 꾸몄습니다.


0

버튼에서 이것을 사용하는 쉬운 방법 :

 var table = $('#example1').DataTable();
 table.row($(`#yesmediasec-${id}`).closest('tr')).remove( ).draw();

example1 = id 테이블. yesmediasec = 행에있는 버튼의 ID

그것을 사용하면 모든 것이 괜찮을 것입니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.