DataTable Üzerinde Distinct ve GroupBy

by Suat TUNCER 31. October 2006 19:16
        Merhaba, T-SQL'de yer alan Distinct fonksiyonu ve Group By clause'unu birçok defa kullanmışızdır, ne iş yaptıklarıda malum yinede şurdan buradan bakılabilir,
        Dönelim konumuza sorun şuki DataTable'e çekilen veriler üzerinde bu fonksiyonları uygulamak hazır fonksiyonlarlar mümkündeğil ve .Net Framework ne yazıkki bu konuda hazır bir fonksiyon içermediği için iş başa düştü, kendi faonksiyonumu hazdım buraya ekliyorum, kullanabilirsiniz

 

        Şimdi bu fonksiyon ne iş yapar; Diyelimki veritabanından verilerimizi çekmişiz ama öyle bir select sorgusuyla değil, iç içe sorgular, fonksiyonlar, hesaplamalarla çekmişiz. Bu titiz çalışmanın sonucu elimizde sadece bir tablo var fekat biz bu tablo üzerinde bir daha çalışmak istiyoruz. Örneğin verileri bir daha gruplamak istiyoruz fakat bunu T-SQL ile yapamıyoruz çünkü yazdığımız sorgu buna izin vermiyor. Çok abartı bir örnek oldu değilmi, yok yok hiçte öyle olmadı,değil çünkü, aynı böyle bir durumla karşılaştım bugün ve oturdum böyle bir fonksiyon yazdım.

public class SharpSql
{
    public SharpSql(){}

    public DataTable Distinct(DataTable dataSource,
                                 string distinctColumn)
    {
        DataTable returnTable=new DataTable("distinctTable");
        /*
         * kaynak tablodaki sütunları
         * geri dönecek tabloya kopyalıyorum
         * böylece direkt satır eklenirken
         * sütunların birebir eşleşmesini sağlıyorum
         */
        foreach(DataColumn c in dataSource.Columns)
        {
            returnTable.Columns.Add(c.ColumnName,c.DataType);
        }
        bool hasRow=false;
        foreach(DataRow selectedRow in dataSource.Rows)
        {
            /*
             * kaynak tabloda herbir satırı seçip
             * sonuçların bulunduğu tabloda benzerinin
             * olup olmadığına bakıyorum
             */
            foreach(DataRow r in returnTable.Rows)
            {
                /*
                 * eşleşen sonuçlar bulunursa,
                 * satırın var olduğu anlamında
                 * hasRow=true yapıyorum
                 */
                if(r[distinctColumn].ToString() ==
                    selectedRow[distinctColumn].ToString())
                {
                    hasRow=true;
                    break;
                }
            }
            /*
             * Seçili satır bulunmamışsa hasRow değeri
             * varsayılan olarak false olacağı için,
             * satır ekleniyor
             */
            if(hasRow==false)
                returnTable.Rows.Add(selectedRow.ItemArray);
            hasRow=false;
        }
        return returnTable;
    }
    /*
     * her bir sütunun kendi toplamına ihtiyacım vardı
     * ve normal DataColumn'da böyle bir değer yoktu
     * bir class oluşturup inherit ettikten sonra
     * Toplam property'sini ekledim
     */
    public class GroupColumn:System.Data.DataColumn 
    {
        private double dblToplam;
        public GroupColumn(string columnName)
        { this.ColumnName=columnName; }

        public double Toplam
        {
            get{return dblToplam;}
            set{dblToplam=value;}
        } 
    }
    /*
     * ve yine tip dönüşümlerinde normal DataColumnCollection,
     * inherits edilmiş GroupColumn classında
     * tip dönüşüm hatası verdiğinden bunun yerine
     * kendi GroupColumnCollections'ımı yazdım 
     */
 
    public class GroupColumnCollection:CollectionBase 
    {
        public GroupColumnCollection(){}
        public void Add(GroupColumn obj)
        { this.InnerList.Add(obj);} 
    }

    public DataTable GroupBy(DataTable dataSource,
                     GroupColumnCollection columns,
                                 string groupByColumn)
    {
        /*
         * Gruplanırken, unique kolonlara göre
         * istenilen toplamlar alınıyor
         * Unique(benzersiz) sütunlar yazdığım
         * Distinct fonksiyonu ile eldeediliyor
         */
        DataTable tableDistinct;
        tableDistinct=this.Distinct(dataSource,groupByColumn);
        /*
         * dataview kullanılıyorki her bir 
         * groubBy kolonuna göre 
         * ayrı ayrı sonuçlar alınsın diye
         */
        DataView viewResult=new DataView();
        viewResult.Table=dataSource;
        DataTable returnTable=new DataTable("groupTable");
        DataRow returnRow;
        //Sütünlar ekleniyor
        foreach(GroupColumn c in columns)
        {
            returnTable.Columns.Add(c.ColumnName,c.DataType);
        }
        //Adet sütünü ekleniyor
        returnTable.Columns.Add("Adet");
        //Gruplamada kullanılan sütunu ekliyorum
        returnTable.Columns.Add(groupByColumn);
        //Toplamları hesaplayp ekliyorum
        foreach(DataRow r in tableDistinct.Rows)
        {
            viewResult.RowFilter =groupByColumn + " = '" 
                        + r[groupByColumn].ToString() + "'";
            //Yeni bir satır oluşturuyorum
            returnRow = returnTable.NewRow();
            /*
             * dataviewe rowfilter uygulandıktan sonra 
             * kalan satırlar geziliyor ve 
             * toplamları hesaplayıp
             * değerleri yeni satıra atıyorum
             */
            for(int  i=0;

                        i<=viewResult.Count;

                        i++)
            {
                foreach(GroupColumn c in columns)
                {
                    c.Toplam += Convert.ToDouble(viewResult[i][c.ColumnName]);
                    returnRow[c.ColumnName]=c.Toplam;
                }
            }
            //Grup adı ve adetini ekliyorum
            returnRow[groupByColumn]=r[groupByColumn].ToString();
            returnRow["Adet"]=viewResult.Count;
            //Son olarak satırı tabloya ekliyorum
            returnTable.Rows.Add(returnRow);
        }
        return returnTable;
    }
}


        Bu kadar yazdıktan sonra hadi ne haliniz varsa görün demiycem elbette, şimdi bunu örnek bir uygulamada kullanarak ne işe yaradığını tam olarak anlatayım, Yeni bir WindowsApplication projesi açıp, yukardaki Classımızı orya ekliyelim, ardında forma 2 DataGrid 3 adette button ekleyip aşşağıdaki kodları yazıyoruz,


 

/

/yukardaki classtan bir instance oluşturuyorum
SharpSql csSql=new SharpSql();

//verileri getir butonu
private void button1_Click(object sender, System.EventArgs e)
{
    SqlConnection cn=new SqlConnection(
                "Data Source=.;
                 Initial Catalog=Northwind;
                 Integrated Security=SSPI;"
);
    SqlCommand cmd=new SqlCommand("select * from [Order Details]",cn);
    SqlDataAdapter da=new SqlDataAdapter(cmd);
    DataSet ds=new DataSet();
    da.Fill(ds,"siparisDetay");
    dataGrid1.DataSource=ds.Tables[0];
}

//Distinct butonu

private void button2_Click(object sender, System.EventArgs e)
{
    /*
     * tekrar eden kayıtlar eleniyor

     * ve sonuç bir DataTable olarak döndüğü için
     * DataSet'e ekliyorum
     */
    string distinctColumn;
    distinctColumn=((DataTable)dataGrid1.DataSource).Columns[dataGrid1.CurrentCell.ColumnNumber].ColumnName ;
    dataGrid2.DataSource=csSql.Distinct((DataTable)dataGrid1.DataSource ,distinctColumn);
}

//Group By butonu

private
void button3_Click(object sender, System.EventArgs e)
{
    /* 
     * Gruplandırmada kullanılacak sütun ve 
     * toplamların alınacağı sütunları ekliyorum
     * sonuç yine datatable olarak döndüğünden
     * onuda dataset'e ekliyorum
     */
    SharpSql.GroupColumnCollection col=new SharpSql.GroupColumnCollection();
    SharpSql.GroupColumn obj;
    obj=new SharpSql.GroupColumn("UnitPrice");
    col.Add(obj);
    obj=new SharpSql.GroupColumn("Quantity");
    col.Add(obj);                
    string groupColumn;
    groupColumn=((DataTable)dataGrid1.DataSource).Columns[dataGrid1.CurrentCell.ColumnNumber].ColumnName;
    dataGrid2.DataSource=csSql.GroupBy((DataTable)dataGrid1.DataSource,col,groupColumn);
}


        Programı çalıştırdığımızda öncelikle Verileri Getir butonunu tıklıyoruz gelen veriler sadece select sorgumusun sonuçları, ProductID sütununa göre sıralayınca anlaşılacaktır zaten, ProductID stünunu seçip Distinct butonuna tıklayınca sonuçlar dataGrid2'de görünecektir tekrar edilen satırlar elendi


    Group By butonuna tıladığımızda ise bu defata, kodlarımızda belirttiğimiz Quantity ve UnitPrice sütunlarındaki değerlerin ProductID'ye göre toplamları görünecektir




       Benimde en çok ihtiyaç duyduğum kısım GroupBy kısmıydı ve bu işimi oldukça gördü umarım sizlerinde işine yarar (biraz uzun oldu sanki Foot in mouth )

 

 

 

Tags: , , ,

C# | Database