DataTable से विशिष्ट पंक्तियों को हटाना


85

मैं DataTable से कुछ पंक्तियों को हटाना चाहता हूं, लेकिन यह इस तरह एक त्रुटि देता है,

संग्रह को संशोधित किया गया था; गणना संचालन निष्पादित नहीं हो सकता है

मैं इस कोड को हटाने के लिए उपयोग करता हूं,

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

तो, समस्या क्या है और इसे कैसे ठीक किया जाए? आप किस विधि की सलाह देते हैं?

जवाबों:


169

यदि आप किसी संग्रह से किसी आइटम को हटाते हैं, तो उस संग्रह को बदल दिया गया है और आप इसके माध्यम से गणना करना जारी नहीं रख सकते।

इसके बजाय, एक लूप का उपयोग करें, जैसे:

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

ध्यान दें कि आप वर्तमान सूचकांक को हटाने के बाद एक पंक्ति को छोड़ने से बचने के लिए रिवर्स में पुनरावृत्ति कर रहे हैं।


@ शलस्टर ने मुझे हरा दिया! (मैं अपने [ii]करने के लिए बदल दिया [i], लेकिन :-)
विडोअर

11
यह गलत है। आप कर सकते हैं पंक्तियों को हटाने के दौरान एक मेज के माध्यम से लूप करने के लिए एक foreach का उपयोग करें। स्टीव द्वारा उत्तर देखें ।
अलेक्जेंडर गार्डन

3
इस उत्तर में @ bokkie का उत्तर भी शामिल होना चाहिए। यदि हम DataTableबाद का उपयोग करते हैं , तो इसका अपवाद फेंक दिया जाएगा। Remove()स्रोत पर कॉल करने का सही तरीका होगा DataTable- dtPerson.Rows.Remove(dr)
Code.me

यदि आप डेटाबेस सर्वर में टेबल को अपडेट करने के लिए डेटाटेबल का उपयोग कर रहे हैं, तो @Steve के पास बेहतर उत्तर है। आप पंक्तियों को हटाए गए के रूप में चिह्नित कर सकते हैं, पंक्तियों को अपडेट कर सकते हैं, और सभी पंक्तियों को एक ही लूप में जोड़ सकते हैं। Db तालिका में परिवर्तन करने के लिए आप SqlAdapter का उपयोग कर सकते हैं। यह देखते हुए कि समस्या कितनी बार उत्पन्न होती है, पूरी प्रक्रिया बहुत अधिक जटिल है जितना आपको लगता है कि यह होना चाहिए, लेकिन यह काम नहीं करता है। अगर मैं DataTable के लेन-देन की प्रकृति का लाभ नहीं उठाने वाला था, तो मैं सिर्फ एक ऑब्जेक्ट संग्रह और namco या Widor दृष्टिकोण का उपयोग करूंगा।
BH

विलोपन को प्रभावी करने के लिए Delete()कॉल का उपयोग करने की आवश्यकता नहीं AcceptChanges()है?
ब्रॉट्स वेम्ब

128

इससे पहले कि हर कोई ' आप किसी एन्यूमरेशन में बैंड्स को डिलीट नहीं कर सकता' बैंडवागन पर, आपको पहले यह महसूस करना होगा कि डेटाटेबल्स ट्रांजेक्शनल हैं , और जब तक आप एक्सेप्चेंज () नहीं कहते तब तक तकनीकी रूप से बदलाव न करें।

यदि आप डिलीट करते समय इस अपवाद को देख रहे हैं , तो आप पहले से ही एक लंबित-परिवर्तन डेटा स्थिति में हैं । उदाहरण के लिए, यदि आपने अभी डेटाबेस से लोड किया है, तो डिलीट कॉलिंग एक अपवाद को फेंक देगी यदि आप एक फॉरेक्स लूप के अंदर थे।

परंतु! परंतु!

यदि आप डेटाबेस से पंक्तियाँ लोड करते हैं और फ़ंक्शन को ' AcceptChanges () ' कहते हैं, तो आप उन सभी परिवर्तनों को DataTable में कर देते हैं। अब आप () हटाएँ दुनिया में एक देखभाल के बिना बुला पंक्तियों की सूची के माध्यम से पुनरावृति सकता है क्योंकि यह बस कान के निशान हटाने के लिए पंक्ति है, लेकिन प्रतिबद्ध नहीं है जब तक आप फिर से फोन AcceptChanges ()

मुझे एहसास है कि यह प्रतिक्रिया थोड़ी दिनांकित है, लेकिन मुझे हाल ही में इसी तरह के मुद्दे से निपटना पड़ा और उम्मीद है कि यह 10-वर्षीय कोड पर काम करने वाले भविष्य के डेवलपर के लिए कुछ दर्द बचाता है :)


Ps यहाँ जेफ द्वारा जोड़ा गया एक सरल कोड उदाहरण है :

सी#

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()

सी # संस्करण के लिए सिर्फ
बग्लवर

2
और भी अधिक उपयोगी (मुझे लगता है) बदलने के object row_loopVariable in ds.Tables(0).RowsलिएDataRow row in ds.Tables(0).Rows
BugLover

2
Ffs, इसने मुझे एक बुरे सपने की तैनाती के दौरान बचाया है। आप सभी बियर के लायक हैं!
जेम्स लव


अच्छा कोड है। एक चीज, सी # में एक के 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 क्लास के डिलीट मेथड को भी कॉल कर सकते हैं। कॉलिंग डिलीट डिलीट को कॉल करने और फिर AcceptChanges को कॉल करने के समान है। डेटाटॉइकोलेक्शन ऑब्जेक्ट के माध्यम से पुनरावृति करते हुए निकालें को फॉर्च लूप में नहीं बुलाया जाना चाहिए। संग्रह की स्थिति को संशोधित करता है। " Msdn.microsoft.com/de-de/library/… चीयर्स देखें ।
एंड्रियास क्रोहन

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

मुझे आशा है कि यह आपकी मदद करेगा


1
कमांड विधि drow.Delete();नहीं drow.delete();है .net btw में संवेदनशील मामला है
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 पंक्ति संग्रह को सूची में बदलें :

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

1

समस्या कहां है: फ़ॉरच लूप के अंदर संग्रह से आइटम हटाना निषिद्ध है।

समाधान: या तो ऐसा करें जैसे कि विधुर ने लिखा है, या दो छोरों का उपयोग करें। 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वांछित परिणाम, स्पष्ट स्रोत DataTableऔर mergeअस्थायी DataTableसे स्रोत एक में परिणाम के साथ स्रोत की प्रतिलिपि बनाता हूं ।

नोट : बजाय के लिए खोज Joeमें DataRowकहा जाता है nameआप सभी रिकॉर्ड जिसका नाम नहीं लिए खोज करने के लिए है Joe(खोज के छोटे विपरीत)

उदाहरण है ( 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अगर कोई परिणाम नहीं है (उदाहरण के लिए, यदि आपके dtPersonपास name Joeयह अपवाद नहीं होगा, तो आप अपवाद नहीं फेंकेंगे), तो मैंने उपयोग किया है , इसलिए आप अपनी तालिका के साथ कुछ भी नहीं करते हैं, यह अपरिवर्तित रहता है।


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

आप डेटा तालिका से आईडी कॉलम प्राप्त करने और निकालने के लिए यह प्रयास करते हैं

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 सर्वर, तो AcceptChangesमैन्युअल रूप से कॉल करने की गारंटी होगी कि परिवर्तन कभी भी SQL सर्वर पर सहेजे नहीं जाते हैं

इस मुद्दे को और अधिक भ्रमित करने वाली बात यह है कि वास्तव में दो मामले हैं जिनमें अपवाद को फेंक दिया गया है और हमें उन दोनों को रोकना होगा।

1. एक IEnumerable के संग्रह को संशोधित करना

हम किसी इंडेक्स को एनुमरेट किए जा रहे संग्रह में शामिल नहीं कर सकते या हटा नहीं सकते क्योंकि ऐसा करने से एन्यूमरेटर की आंतरिक अनुक्रमण प्रभावित हो सकता है। इसके आसपास पहुंचने के दो तरीके हैं: या तो लूप के लिए अपनी खुद की अनुक्रमणिका बनाएं, या एन्यूमरेशन के लिए एक अलग संग्रह (जो संशोधित नहीं है) का उपयोग करें।

2. हटाए गए प्रविष्टि को पढ़ने का प्रयास

चूंकि डेटाटेबल्स ट्रांजेक्शनल कलेक्शन हैं, प्रविष्टियों को विलोपन के लिए चिह्नित किया जा सकता है लेकिन अभी भी गणना में दिखाई देता है। जिसका अर्थ है कि यदि आप कॉलम के लिए हटाए गए प्रविष्टि को पूछते हैं "name"तो यह एक अपवाद फेंक देगा। जिसका अर्थ है कि हमें यह देखने के लिए जाँचना चाहिए कि क्या dr.RowState != DataRowState.Deletedकिसी कॉलम को क्वेरी करने से पहले।

यह सब एक साथ डालें

हम सभी गड़बड़ कर सकते हैं और मैन्युअल रूप से कर सकते हैं, या हम डेटाटेबल को हमारे लिए सभी काम करने दे सकते हैं और निम्नलिखित करके एसक्यूएल कॉल की तरह स्टेटमेंट लुक और अधिक बना सकते हैं:

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

DataTable के Selectफ़ंक्शन को कॉल करके , हमारी क्वेरी स्वचालित रूप से DataTable में पहले से ही हटाए गए प्रविष्टियों से बचती है। और चूंकि Selectफ़ंक्शन मेल खाता है, इसलिए जब हम कॉल करते हैं तो संग्रह को संशोधित किया जाता है dr.Delete()। मैंने भी कोड शोर के बिना चर चयन के लिए अनुमति देने के लिए स्ट्रिंग प्रक्षेप के साथ चयन अभिव्यक्ति को मसालेदार किया है।


0

इस आसान तरीके से बटन का उपयोग करें:

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

example1 = id टेबल। हांमीडेसेक = पंक्ति में बटन की आईडी

इसका उपयोग करें और हर चीज ठीक होगी

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.