23 Temmuz 2015 Perşembe

Unique olmayan kayıtları topluca silmek (bulk delete non-unique records)

Merhaba arkadaşlar,

Bu yazımda, tabloda Unique olmayan kayıtları toplu olarak silmeyi ele alacağım. Üzerinde çalışacağımız senaryoda, kayıtları daha önce gelişigüzel girilmiş, AirPortCode ve AirPortName adında alanları bulunan bir tablom var. AirPortCode alanı unique olması icab eden bir alan ancak tablo bu şekilde yapılandırılmamış ve bir çok mükerrer kayıt girişi yapılmış. Şu durumda Management Studio(SSMS) ortamında AirportCode alanını primary key yapmak istediğimde SSMS aşağıdaki hatayı dönmekte;

 'AirPorts' table
- Unable to create index 'PK_AirPorts'.
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.AirPorts' and the index name 'PK_AirPorts'. The duplicate key value is (BAY).
Could not create constraint. See previous errors.
The statement has been terminated.


Yani alanın primary key olmasına engel teşkil eden mükerrer ilk kaydı gösterip işlemi yapamadığını söylüyor.

zahmetli de olsa, her bir hata için mükerrer tablo kayıtlarını edit modda açıp silmek istediğimde ise;

---------------------------
Microsoft SQL Server Management Studio
---------------------------
No rows were deleted.

A problem occurred attempting to delete row 1.
Error Source: Microsoft.SqlServer.Management.DataTools.
Error Message: The row value(s) updated or deleted either do not make the row unique or they alter multiple rows(2 rows).

Correct the errors and attempt to delete the row again or press ESC to cancel the change(s).
---------------------------
OK   Help  
---------------------------


silmek istenen kayıttan birebir aynı birden fazla kayıt olduğundan işlemi bu yöntemle de gerçekleştiremiyoruz.


--mükerrer haavalanlarını ve adetlerini tutacağımız değişkenlerimizi tanımlıyoruz.
declare @AirPortCode varchar(3), @AirPortCount int
--mükerrer kayıt kalmayana kadar döngü çalışacak. işlemi cursor kullanarak ta yapabilirsiniz.
while exists(select null from AirPorts group by AirPortCode having COUNT(AirPortCode)>1)
begin
/*mükerrer havaalanı ve ilgili havaalanının mükerrer kayıt sayısı bulunuyor.
kayıt sayısının 1 eksigini alarak tabloda tek kayıt kalmasını sağlamış oluyoruz.
*/

select @AirPortCode = AirPortCode, @AirPortCount = COUNT(AirPortCode) - 1
from AirPorts group by AirPortCode having COUNT(AirPortCode)>1
--bulunan değer kadar kaydı siliyoruz
delete top (@AirPortCount) from AirPorts where AirPortCode = @AirPortCode
end;


artık AirportCode alanını primary key olarak atayabiliriz. Tablo ve data scriptini buradan indirebilirsiniz.

faydalı olması dileğimle,

bir sonraki yazımda görüşmek üzere,
Yaşar Şahin






14 Temmuz 2015 Salı

Sınıf ve Üyelere Açıklama Eklemek

Merhaba arkadaşlar,

özellikle üçüncü parti dll kütüphanerinde kullandığımız fonsiyonların işlevlerini bilmek işimizi kolaylaştırmaktadır. Bu yazıda kendi oluşturacağımız sınıf ve üyelerine açıklama ekleyeceğiz.

Basitçe oluşturulmuş bir toplama fonksiyonu aşağıdaki gibi iken;

public static class Hesaplamalar
    {
        public static double Topla(double p1, double p2)
        {
            return p1 + p2;
        }
    }


 Hesaplamalar statik sınıfı altındaki static Topla fonksiyonunu çağırmak istediğimde intellisense varsayılan açıklaması aşağıdaki resimdeki gibi olmaktadır.

Sınıf ve sınıf üyeleri tanımlalarının hemen üst satırına /// işareti eklediğimizde editör ilgili açıklama şablonunu oluşturmaktadır.Metod açıklamalarına her bir parametre için ayrı ayrı açıklama girilebilmektedir.

    /// <summary>
    /// Hesaplamalar sınıfı
    /// </summary>

    public static class Hesaplamalar
    {
        /// <summary>
        /// parametresine girilen değerleri toplar.
        /// </summary>
        /// <param name="p1">double türünden değerler virgül ile ayrılarak, ya da double bir dizi parametre olarak geçilebilir</param>
        /// <returns>double değer döndürür</returns>

        public static double Topla(params double[] p1)
        {
            return p1.Sum();
        }
    }

editörde metodu kullanmak istediğimizde intellisense açıklamayı getirecektir;


kütüphanenizdeki sınıf ve üyerine açıklamalar girerek hem kendinizin hem de paylaştığınız kullanıcıların sınıf ve üyelerinin amaçlarını hatırlamasını/anlamasını kolaylaştırmış olacaksınız.

Bir sonraki yazımda görüşmek üzere,
Yaşar Şahin


8 Temmuz 2015 Çarşamba

TurboTimer (kalıtımla sınıf genişletme)

Merhaba arkadaşlar,

dün bir blogda, timer komponentinin kullanımında, kod içinde timer çalışma durumunun tespiti için, başka bir nesnenin isminin değiştirilerek kullanıldığını gördüm. Kodu aynen almıyorum ancak temel olarak yapılan şuydu;

if (this.Text == "timer is running")
            {
                timer1.Stop();
                this.Text = "timer stopped";
            }
            else
            {
                timer1.Start();
                this.Text = "timer is running";
            }

böyle bir çözüm üretilmesinin sebebi timer nesnesinin çalışma durumunu gösteren belirgin bir property(özellik) olmaması olmalı diye düşünüp, biraz da can sıkıntısından timer'a bir kıyak geçeyim dedim ve timer sınıfından türettiğim TurboTimer sınıfıyla, timera hem bir özellik ekledim hem de çalışma durumu değişikliklerine abonelik yapılsın deyip bir de event handler(olay tutucu) tanımladım. bakın nasıl yapmışım;

//timerstate numaralandırıcısı ile artık intellisense hizmetimizde olacak.
    public enum TimerState { running, stopped }

    public class TurboTimer: System.Windows.Forms.Timer//temel sınıf adına dikkat çünkü frameworkte birden çok timer sınıfı var.
    {
        //özelliğimin varsayılan değerini atıyorum.
        private TimerState currentState = TimerState.stopped;
        public TimerState CurrentState { get { return currentState; } set { currentState = value; } }
        //özellik durum değişikliklerini izlemek isteyen metodlara tutucu tanımlıyorum.        public event EventHandler<TimerStateChangeEventArgs> TimerStateChanged;
        //özelliğimi temel sınıfın enabled özelliğine göre set ediyorum. burada temel sınıfın Enabled özelliğini ezmemiz gerekti.         public override bool Enabled
        {
            get
            {
                return base.Enabled;
            }
            set
            {
                base.Enabled = value;
                this.CurrentState = (base.Enabled) == true ? TimerState.running : TimerState.stopped;
                /*durum değişikliği oluştuğunda abonelere (subscriber) haber vereceğimiz metodu çağırıyorum.
                 * bu sayede aboneler, kendi abonelik metodlarından durumu değerlendirebilecekler.
                 * TimerStateChangeEventArgs sınıfı ile, aboneye, sınıfım içinden istediğim bilgileri gönderiyorum.
                 */
                OnTimerStateChanged(new TimerStateChangeEventArgs(this.CurrentState));
            }
        }
        //öncelikle abonem var mı diye bakıyorum ki işlemci karavana çalışmasın :)        protected virtual void OnTimerStateChanged(TimerStateChangeEventArgs timerStateChangeEventArgs)
        {
            EventHandler<TimerStateChangeEventArgs> handler = TimerStateChanged;
            if (handler != null)
            {
                handler(this, timerStateChangeEventArgs);
            }
        }
    }
    //aboneye göndermek istediğim bilgileri (olay argümanları), EventArgs sınıfından türetilmiş sınıfta belirtiyorum,
    //yukarıda da tetiklemeden önce doldurmuştuk.
    public class TimerStateChangeEventArgs : EventArgs
    {
        public TimerState CurrentState { get; set; }
        public TimerStateChangeEventArgs(TimerState pState)
        {
            this.CurrentState = pState;
        }
    }

TurboTimer sınıfımız Timer komponentinden ve dolayısıyla Component sınıfından türediğinden, kodu derlediğinizde, Tollboxta TurboTimer komponentini göreceksiniz.

 


böylece sınıfımızı toolboxtan sürükleyerek te kullanabileceğiz. 
Esas olarak burada yapılan, bir sınıfa kalıtım yolu ile ek işlevsellikler kazandırmaktır. Yukarıdaki kodları ve, komponenti kullandığım örnek Windows uygulamasını buradan indirebilirsiniz.

Bir sonraki yazımda görüşmek üzere,
Yaşar Şahin

30 Haziran 2015 Salı

Dinamik Sql (Dynamic Sql) ile Raporlama


Merhaba arkadaşlar,

bu yazımda, özellikle muhasebe programları için çokça talep edilen raporlardan biri olan nakit akış raporunun bir ucundan tutmaya çalışacağız. Oluşturacağım rapor scriptleri dinamik olarak oluşacak. Yani raporumun scripti kod içinde bazı karşılaştırmalar neticesinde adım adım oluşacak.

Öncelikle oluşturacağımız raporu tanımaya çalışalım;

nakit akış raporu, vadeli işlenen (cari) faturaların bir projeksiyonudur diyebiliriz. muhtelif vadelerle işlenmiş alış ya da satış faturaları, vadesi itibarıyla ilgili oldukları ayda tahsil ya da tediye edilecek. yani kestiğim faturalar için tahsilatlarım olacağı gibi bana kesilen faturaların vadeleri geldiğinde de ödemelerim olacak. bu sayede de ilgili dönemlerde firmamın mali durumunu gözlemlemiş olacağım (bunu sadece muhasebeyle sınırlandırmamak lazım. aynı yöntemle siparişlerimi ya da tekliflerimi termin tarihlerine göre gruplaya da bilirdim).

işte bu sebeplerden ötürü faturaları alış/satış tiplerine göre gruplayıp gelecek aylar bazında toplamlarını görmek istemekteyim.

elimizde alış ve satış faturalarını işlediğimiz "Faturalar" adında bir tablomuz var (tüm muhasebe programlarında aşağı yukarı bu tarz bir tablo vardır.).

tablonun yapısı; 



select faturatipi,SUM(tutar) as Toplam from Faturalar where vade>GETDATE() group by faturatipi  sorgusuyla vadesi gelmemiş faturalarımın sadece toplamlarını alabilmekteyim;



bu şekilde aldığım rapor bana yalnız toplam borç alacak durumumu vermekte. bu rakamlara bakarak bu ay ya da önümüzdeki ay yapacağım tahsilatları görmem mümkün olmamakta.

İşte bu amacı karşılayacak rapor şu şekilde olmalı;



talebi açıkça anladığımıza göre artık kodumuzu yazabiliriz;

--bu değişken dinamik scriptimizi adım adım oluşturup atadığımız değişkenimiz.
declare @SQLStr nvarchar(max)='',@ay int=0;
declare @minVade datetime = (select MIN(vade) from Faturalar);
--burada maximum vadeli faturaya kadar kaç ay olduğunu hesaplıyoruz.
declare @aySayisi int = (select DATEDIFF(MONTH,min(vade),max(vade)) from Faturalar);

set @SQLStr = ' 
select 
 
faturatipi, '
/*
scriptimizi toplam ay sayısı kadar döndürüp, her dönüşte fatura vadesinin ilgili ay içerisinde olup olmadığına bakıyorum.
eğer fatura vadesi ilgili ay içerisinde ise tutar değerini alıyorum değil ise 0 alıyorum
*/
while(@ay<=@aySayisi)
begin
 set @SQLStr=@SQLStr+
 'sum(convert(decimal(18,2),case when dateadd(day,DATEDIFF(day,0,vade),0) between '''
--ayın ilk gününü buluyorum

 + 
 CONVERT(varchar(8),dateadd(MONTH,DATEDIFF(MONTH,0,dateadd(MONTH,@ay,@minVade)),0),112)
 + ''' and '''


--ayın son gününü buluyorum  
 + CONVERT(varchar(8),dateadd(month,1+datediff(month,0,dateadd(MONTH,@ay,@minVade)),-1),112)
 + ''' then tutar' 

--sıra geldi oluşturduğumuz alana isim vermeye. ismi "yıl-ay" kombinasyonu olarak ayarlıyorum.   
 set @SQLStr=@SQLStr+ 
 ' else 0.0 end)) as ['
 + datename(MONTH,dateadd(MONTH,@ay-1,@minVade))
 + '-'+ datename(YEAR,dateadd(MONTH,@ay-1,@minVade))
 + ']'
 if @ay<@aySayisi
   set @SQLStr = @SQLStr + ','

 set @ay=@ay+1
end
--son olarak ta dinamik scriptimin çalışacağı kaynak tabloyu gösteriyorum.
set @SQLStr=@SQLStr+ 

from Faturalar
group by faturatipi  

'

exec (@SQLStr)

GO


tablonun create scriptini ve oluşturduğumuz dinamik scripti buradan indirebilirsiniz. bir kaç satır faturatipi, vade ve tutar bilgilerini doldurup kodu deneyebilirsiniz.

İhtiyaçlarımız doğrultusunda scripti geliştirebiliriz. Örneğin, scriptte haftalık bazda yapılacak revizyonla daha kısa dönemli projeksiyonlar yapılması mümkün kılınabilir. ayrıca işin içine farklı para tiplerini dahil ederek, istenen kura ya da fatura ya da vade tarihlerindeki kurlara göre değerlendirmeler yapılabilir.

Bir sonraki yazımda görüşmek dileğimle,
Yaşar Şahin.

24 Haziran 2015 Çarşamba

C# Enum tipleri

Merhaba arkadaşlar,

Bu gün ilk C# makalemi oluşturacağım. değinmek istediğim konu Enum tipleri(numarandırıcılar). Bu tipler sayesinde sınırlı değerlere sahip olabilecek değişkenleri temsil edebilirler. bu sayede değişkenin alabileceği değerleri hem kodlamak zorunda kalmaz, hem de olası değer durumlarına kod yazacağımız durumlarda değeri hatırlamak zorunda kalmayız(örn. switch döngüleri.

enum Gunler
        {
            pazartesi, salı, çarşamba, perşembe, cuma
        } 


şeklinde tanımladığımız enum tipi için .Net ide ortamında "sw" yazıp tab yaparsak ide bize olası değerlerin tek tek işlendiği swith-case yapısını oluşturacaktır.).

Benim burda değinmek istediğim Enum tip değerlerini alıp kontrol içeriğini doldurmak ve kontrolden seçilen değerin enum tipine dönüştürülerek kullanılması olacak. Konunun daha iyi anlaşılması için koda geçelim;



using System;
using System.Drawing;
using System.Windows.Forms;

namespace EnumTypes
{
    class Colours
    {
        private KnownColor _color;
        public String Colour { 

get { return _color.ToString(); } 
set { _color = (KnownColor)Enum.Parse(typeof(KnownColor), value); } 
}
/*Tüm anlatılanların özeti burası. Burada, sınıfımız "Colour" özelliği ile, aldığı aldığı string değeri KnownColor enum tipine dönüştürmekte ve bunu _color isimli private KnownColor tipli üyeye atamaktadır.*/

        public String[] ColorNames()
        {
           return Enum.GetNames(typeof(KnownColor));
        }
//bu fonksiyonda parametresine aldığı controlü _color private üyesi değeriyle boyuyor.
        public void PaintMe(object sender)
        {
            Control ctrl = sender as Control;
            if (ctrl == null)
                return;

            try
            {
                ctrl.BackColor = Color.FromKnownColor(_color);
            }

            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }
        }
    }
}



uygulama koduda aşağıda ekliyorum.

Örnek çok anlamlı olmasa da enum tiplerinin anlaşılması açısından faydalı olacaktır.

Uygulama kodunu ve tüm blog projelerini buradan indirebilirsiniz.

Faydalı olması dileğimle,
Yaşar Şahin

using System;
using System.Drawing;
using System.Windows.Forms;

namespace EnumTypes
{
    public partial class frmEnum : Form
    {
        Colours _renkler;

        public frmEnum()
        {
            InitializeComponent();

            comboBox1.SelectedIndexChanged += new EventHandler(comboBox1_SelectedIndexChanged);
            _renkler = new Colours();

            comboBox1.Items.AddRange(_renkler.ColorNames());
        }

        void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            toolStripStatusLabel1.Text = "";
            try
            {
                _renkler.Colour = ((ComboBox)sender).SelectedItem.ToString();
                _renkler.PaintMe(comboBox1);
            }
            catch (Exception ex)
            {
                toolStripStatusLabel1.Text = ex.Message;
            }
        }
    }
}

19 Haziran 2015 Cuma

t-Sql Split

Merhaba arkadaşlar,

ilk makalemde, bir sonraki makalemde paylaşmak istediğim, veritabanındaki nesneleri(görünümler, prosedürler, fonksiyonlar gibi) güncellemek için oluşturduğum, bu nesnelerin oluşturma scriptlerini döndüren bir aracın kullandığı tablo değerli bir fonksiyonu (table-valued function) oluşturacağız. Oluşturacağımız bu fonksiyonun ayrı olarak ta işime yaradığını belirtmek isterim.

Aslında bu fonksiyon .Net programcılarının yapancı olmadıkları String sınıfının Split fonksiyonu benzeri olacak. .Net tarafında bu fonksiyonun ilk varyasyonunun görevi;

        // Summary:
        //     Returns a string array that contains the substrings in this instance that
        //     are delimited by elements of a specified Unicode character array. A parameter
        //     specifies the maximum number of substrings to return.
        public string[] Split(char[] separator, int count);

şeklinde belirtilmiş. Bizim oluşturacağımız fonsiyon ikinci parametredeki gibi bir sınırlandırıcı içermeyecek.

Buyrun başlayalım;

create function dbo.fn_Split
(
  @metin varchar(max)
  , @ayrac char(1)
)
returns @rTable table(id int identity(1,1),data varchar(64))
--sonuç tablomuzu tanımlıyoruz
as
begin

--@metin parametresi @ayraç parametresini içerdiği sürece döngü çalışacak.
while(CHARINDEX(@ayrac,@metin)>0)
begin
--ilk sonuç tabloya işleniyor
insert @rTable (data) values (ltrim(rtrim(SUBSTRING(@metin,0,CHARINDEX(@ayrac,@metin)))))
--bulunan alt metin @metin parametresinden çıkarılmalı.
set @metin = SUBSTRING(@metin,CHARINDEX(@ayrac,@metin)+1,LEN(@metin))
end
--@metin parametresinin son hali tabloya işleniyor. döngü dışındaki bu konumda @metin artık ayraç içermemektedir.
insert @rTable (data) values (ltrim(rtrim(SUBSTRING(@metin,1,LEN(@metin)))))
return
end;


çalışmasını görmek için;
select * from dbo.fn_Split('paylaşımın faydalı olması dileğiyle', N' ')

bir sonraki paylaşımda görüşmek dileğiyle.
Yaşar Şahin

Toplu Sql nesne scriptleri

Merhaba arkadaşlar,

otomatik olarak güncellenmeyen yazılımlar için, benim gibi yazılım desteğinde çalışanların en sık yaptıkları şey veritabanı neslelerini(görünümler, prosedürler, fonksiyonlar gibi) güncellemek olmaktadır. Sürekli değişen prosedürler, görünümler, fonksiyonların, uygulamalarla birlikte güncellenmeleri gerektiği muhakkak. Kaynak veritabanından her nesnenin scriptini alıp bunu hedef veritabanında tek tek çalıştırmak uzun ve dikkat gerektiren bir iştir. çünkü güncellemeyi atladığınız herhangi bir nesne size müşteri tarafından hata bildirimi olarak geri döner. Bu makalemde bu işlemleri kolaylaştırmak adına oluşturduğum bir prosedürü sizinle paylaşmak istiyorum.

Esas olarak bir sql prosedürü olan sp_helptext'in istenen tüm nesleler için çalışmasını sağlayacağız. Prosedür parametre olarak aldığı text içerisinden ,(virgül)'le ayrılan her bir nesne için sp_helptext sonucunu, tek alanı olan geçici tabloya işleyecek ve nihayetinde bu tabloyu döndürecek.

create procedure [dbo].[sp_returnObjectScripts]     
@objName nvarchar(512)  sp
as 
begin 

--bu tabloya sp_helptext sonuçlarını işleyeceğiz.
create table #tmpUpdatedText(clText nvarchar(max) collate database_default) 
 
declare @parobjName nvarchar(64) 
/*kürsör içinde her bir nesne ismini bu değişkene atıp, veritabanında varlığını kontrol eden ve
varsa silen kodu tabloya ekliyoruz.
*/

DECLARE c1 CURSOR FOR
    select data from fn_split(@objName,',') s
    join sys.objects o on o.name = s.data
    --kürsörümüz parametre içindeki tüm nesneler için dönecek. sys.object eşleşmesi ile nesnenin varlığını da kontrol etmiş oluyoruz.
 
    OPEN c1; 
    FETCH NEXT FROM c1 INTO @parobjName;    
 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
   insert #tmpUpdatedText(clText) 
/*kürsör içinde her bir nesne ismini bu değişkene atıp, veritabanında varlığını kontrol eden ve
varsa silen kodu tabloya ekliyoruz.*/  

   select  
'IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[dbo].['+  @parobjName + ']'')) ' + CHAR(13) + CHAR(10) +  
'DROP ' +  
   (CASE WHEN type_desc in ('SQL_STORED_PROCEDURE') then 'PROCEDURE ' 
   WHEN type_desc in ('SQL_SCALAR_FUNCTION','SQL_TABLE_VALUED_FUNCTION','SQL_INLINE_TABLE_VALUED_FUNCTION') then 'FUNCTION ' 
   WHEN type_desc in ('VIEW') then 'VIEW ' END) 
   + '[dbo].[' + @parobjName + ']' 
   from sys.objects where name = @parobjName; 
   insert #tmpUpdatedText(clText) 
   values 
   ('GO'); 
--burda da nesneyi sp_helptext prosedürüne parametre olarak verip sonucu yine geçici tabloya yazıyoruz.
   insert #tmpUpdatedText(clText) 
   exec sp_helptext @parobjName; 
   insert #tmpUpdatedText(clText) 
   values 
   ('GO'); 
     FETCH NEXT FROM c1 INTO @parobjName; 
     END; 
 
    CLOSE c1; 
DEALLOCATE c1; 
 
select * from #tmpUpdatedText; 
drop table #tmpUpdatedText;  
end;


böylece güncellemek istediğimiz prosedür view yahut fonksiyonları oluşturduğumuz bu araca parametre olarak verip toplu olarak oluşturma scriptlerine sahip olacağız.

örnek kullanım:
sp_returnObjectScripts 'fn_split,sp_helptext,sp_returnObjectScripts'

bir sonraki paylaşımda görüşmek dileğiyle.
Yaşar Şahin