none
iki tarih arasında sorun RRS feed

  • Soru

  • veritabanı na tarihleri kayıt ederken "convert(varchar, getdate(), 104)" bu şekilde kayıt ediyorum daha sonra arama yaparken datetimepicker dan tarihleri seciyorum aradığım zaman ogünlerin hepsini getiriyor örnegin 29.03.2012 ile 31.03.2012 arasındaki verileri arıyorum çıkan sonuclar içinde 29.02.2012 - 30.01.2012- 31.01.2012-30.03.2012 tarihlerini de getiriyor yani geçmiş aylardaki o tarihleride getiriyor.

    sorguları sürekli degiştirdim ama bir türlü yapamadım .

    .............where  SipGondermeTarihi Between '29.03.2012' AND '31.03.2012' 

    ............. where SipGondermeTarihi>='29.03.2012' and SipGondermeTarihi<='31.03.2012'

    .............. where SipGondermeTarihi>=convert(varchar, '29.03.2012', 104) and SipGondermeTarihi<=convert(varchar, '31.03.2012', 104)

    yukardaki gibi sorgular denedim ama sonuç alamadım. Sağlıklı bir çözüm üretmem için ne yapmalıyım.

    2 Nisan 2012 Pazartesi 17:47

Yanıtlar

  • Bence de tarih/saat degerlerini tarih/saat olarak kaydet. Illa ki karakter olarak kaydedeceksen de 104 kullanma, 102, 120 gibi sonucun degisik sistemlerde yanlis algilanmasini onleyecek formatlar kullan. 104 ile:

    05.04.2012

    Bunu dogrudan internet sayfasina koysan ve sorsan,  5 Nisan 2012 mi, yoksa 4 Mayis 2012 mi yaziyor? Kimin baktigina gore degisik cevaplar alirsin. Oysa 102/120 ile:

    2012.04.05 ve 2012-04-05 13:45:50

    gibi degerler alirsin ki dunyanin her yerinde 5 Nisan olarak degerlendirilir.

     

    Karakter olarak sakladiysan, yine ayni sekilde cevirmen gerekecek, cevirmezsen yaptigin sorgu suna benziyor:

    where  '30.01.1990' Between '29.03.2012' AND '31.03.2012'

    sonuc dogru, '30.01.1990' karakter karsilastirmada alfabetik olarak '29.03.2012' ile '31.03.2012'  arasinda. 102/120 kullansaydin ve diger tarafi da 2012.03.29 and 2012.03.31 yazsaydin istedigin sonuclari alacaktin (102/120 siralanabilir bir tarih/saat formati). Iyi ki de boyle yapip dogru sonuclari almamissin. Sen en iyisi dogrudan tarih/saat sakla. Kesinlikle ceviremem su anda tarih/saate dersen o zaman sakladigin sekilde geriye tarih/saat convert et once.

    where 
       CONVERT(DATETIME,SipGondermeTarihi,104) >= convert(DATETIME,'29.03.2012',104) and
       CONVERT(DATETIME,SipGondermeTarihi,104) < convert(DATETIME,'01.04.2012',104)

    Gelelim isin bir baska boyutuna. Tarih araligindaki sorgularda Between kullanma. Eger tarih/saat degelerinin saat bilgisi icermediginden %100 emin olabiliyorsan o zaman between kullanmakta sakinca yok. Ancak genelde bu tip kayitlar sadece tarihi degil tarih ve saati icerir (birisi gunun birinde senin "Siparis Gonderme Tarihi"ni saati de bilelim diye degistirirse ister istemez hatali sonuclar almaya baslarsin). BETWEEN yerine >= baslangic < bitis seklinde kullan. Bitis degeri biraz ozel, istedigin son degerden buyuk en kucuk deger. Yukaridaki ornekte 31 Mart 2012 degil, 1 Nisan 2012.  1 Nisan 2012 demek  1 Nisan 2012 00:00 demek. Sen de tum Mart ayindakileri istedigine gore 31 Mart 2012 23:59:59 da gonderilen kayit bile sonuclarda yer alacak demektir ki istedigimiz tam olarak bu.

    Diger bir boyutta bu sorguda 29.03.2012 31.03.2012 senin dedigin gibi bir datetimepicker'dan geliyor ve sen gereksiz yere once karaktere cevirip bu sorguyu hazirliyorsun. Oysa hicbir cevirim yapmamak daha dogru. DatetimePicker olduguna gore dili yuzde yuz kestirememekle birlikte bir .Net dili olabilmesi ihtimali var. O zaman da dogrudan soyle yap derim:

    1) Veri tabaninda karakter olarak ve degistirme ihtimali yok ise:

    DateTime baslangic = dtpBaslangic.Value;
    DateTime bitis = dtpBitis.Value.Date.AddDays(1); // 31 Mart secildiyse 1 Nisan yap
    
    DataTable tbl = new DataTable();
    using (SqlConnection con = new SqlConnection(...))
    {
      SqlCommand cmd = new SqlCommand(
      "select * from tabloAdi where convert(datetime, SipGondermeTarihi, 104) => @baslangic and convert(datetime, SipGondermeTarihi, 104) < @bitis", con);
      cmd.Parameters.AddWithValue( "@baslangic", baslangic);
      cmd.Parameters.AddWithValue( "@bitis", bitis);
      con.Open();
      tbl.Load( cmd.ExecuteReader() );
      con.Close();
    }
    
     

    2) Veri tabaninda ideal olarak Date ya da DateTime tipi kullandiysan, ve daha da iyisi kodunda Linq kullaniyorsan:

    DateTime baslangic = dtpBaslangic.Value;
    DateTime bitis = dtpBitis.Value.Date.AddDays(1); // 31 Mart secildiyse 1 Nisan yap
    
    var sorgu = from veri in db.TabloAdi
       where veri.SipGondermeTarihi >= baslangic &&
       veri.SipGondermeTarihi < bitis
       select veri;
    
    // sorgu bir IQueryable ve henuz server'a gitmedi. 
    // Istersen ek filtre, siralama vs ekleyip kullanabilirsin
    // ornek:
    
    var sorguSirali = sorgu.SortBy( veri => veri.SipGondermeTarihi );
    
    // bir DataGridView'un varsa ornek olarak:
    dataGridView1.DataSource = sorguSirali;
    
    // sorguSirali.ToString() ile de SQL'i alabilirsin.
    // ya da db.GetCommand( sorguSirali ) ile daha detayli

    • Yanıt Olarak İşaretleyen Serkan Bark 9 Nisan 2012 Pazartesi 07:07
    5 Nisan 2012 Perşembe 11:46
  • Yani olay basit aslinda, gecersiz olan kayitlarini duzelt ve datetime kullan. Gecici olarak soyle bir fonksiyon yaratip hatayi olusturan kayitlari bulma sansin var (fonksiyon her turlu durumu dusunmuyor, gg.aa.yyyy formatinda veri oldugunu varsayiyor - TSQL ile bunlari yapmak olum, CLR ile yapsan daha iyi)

    CREATE FUNCTION ValidateDate ( @check VARCHAR(10) )
    RETURNS BIT
    AS 
        BEGIN
            DECLARE @gun VARCHAR(2) ,
                @ay VARCHAR(10) ,
                @yil VARCHAR(10) ,
                @valid BIT;
            SET @gun = CAST(SUBSTRING(@check, 1, CHARINDEX('.', @check) - 1) AS INT);
            SET @check = SUBSTRING(@check, CHARINDEX('.', @check) + 1, 10);
            SET @ay = CAST(SUBSTRING(@check, 1, CHARINDEX('.', @check) - 1) AS INT);
            SET @yil = CAST(SUBSTRING(@check, CHARINDEX('.', @check) + 1, 10) AS INT);
    
            SET @valid = CASE WHEN @ay < 1
                                   OR @ay > 12 THEN 0
                              WHEN @gun < 1
                                   OR @gun > ( CASE WHEN @ay = 2
                                                    THEN CASE WHEN ( @yil % 4 = 0
                                                                  AND @yil % 100 <> 0
                                                                  )
                                                                  OR @yil % 400 = 0
                                                              THEN 29
                                                              ELSE 28
                                                         END
                                                    WHEN @ay IN ( 4, 6, 9, 11 )
                                                    THEN 30
                                                    ELSE 31
                                               END ) THEN 0
                              ELSE 1
                         END;
    
            RETURN @valid;
        END
    

    Bunun yarattiktan sonra:

    Select sipGondermeTarihi from tabloAdi where dbo.ValidateDate( sipGOndermeTarihi ) = 0

    Iyi sanslar.

    Not: Dedigim gibi mesela LinqPAd ile tum sipGondermeTarihi kayitlarini alsan .Net koduyla daha kolay kontrol edersin.

    • Yanıt Olarak İşaretleyen Serkan Bark 9 Nisan 2012 Pazartesi 07:07
    6 Nisan 2012 Cuma 13:50

Tüm Yanıtlar

  • Merhaba Mehmet,

    Veritabanına tarih bilgisini kaydederken varchar() karakter tipine çevirip kaydettiğin için bu problemi yaşıyorsun.

    Veri haliyle karakter tipinde olunca, karşılaştırma da string tipinde oluyor, beklediğin sonucu vermiyor

    Bence veriyi sakladığın alanın tipini DATE veya DATETIME olarak çevir. Kaydederken CONVERT kullanmadan doğrudan kaydet.

    İkinci çözüm de veriyi karşılaştırmadan önce DATETIME tipine çevirmen. Ama bu sorgu performansını çok düşürür. Index kullanımını engeller.

    En doğru çözüm, veriyi doğru tip olan DATE tipinde kaydetmen.


    SQL Server, SQL Server 2012 Denali and T-SQL Tutorials

    3 Nisan 2012 Salı 05:29
  •  eralper , hocam cevabınız için tşk ederim yalnız sizin dediğiniz gibi "veriyi karşılaştırmadan önce DATETIME tipine çevirme" ye çalıştım ama bir türlü çalıştıramadım.Sorgu çalışıyor ama sonuc gelmiyor.Cevirme konusunda küçük bir örnek verirmisiniz.
    4 Nisan 2012 Çarşamba 17:45
  • Bence de tarih/saat degerlerini tarih/saat olarak kaydet. Illa ki karakter olarak kaydedeceksen de 104 kullanma, 102, 120 gibi sonucun degisik sistemlerde yanlis algilanmasini onleyecek formatlar kullan. 104 ile:

    05.04.2012

    Bunu dogrudan internet sayfasina koysan ve sorsan,  5 Nisan 2012 mi, yoksa 4 Mayis 2012 mi yaziyor? Kimin baktigina gore degisik cevaplar alirsin. Oysa 102/120 ile:

    2012.04.05 ve 2012-04-05 13:45:50

    gibi degerler alirsin ki dunyanin her yerinde 5 Nisan olarak degerlendirilir.

     

    Karakter olarak sakladiysan, yine ayni sekilde cevirmen gerekecek, cevirmezsen yaptigin sorgu suna benziyor:

    where  '30.01.1990' Between '29.03.2012' AND '31.03.2012'

    sonuc dogru, '30.01.1990' karakter karsilastirmada alfabetik olarak '29.03.2012' ile '31.03.2012'  arasinda. 102/120 kullansaydin ve diger tarafi da 2012.03.29 and 2012.03.31 yazsaydin istedigin sonuclari alacaktin (102/120 siralanabilir bir tarih/saat formati). Iyi ki de boyle yapip dogru sonuclari almamissin. Sen en iyisi dogrudan tarih/saat sakla. Kesinlikle ceviremem su anda tarih/saate dersen o zaman sakladigin sekilde geriye tarih/saat convert et once.

    where 
       CONVERT(DATETIME,SipGondermeTarihi,104) >= convert(DATETIME,'29.03.2012',104) and
       CONVERT(DATETIME,SipGondermeTarihi,104) < convert(DATETIME,'01.04.2012',104)

    Gelelim isin bir baska boyutuna. Tarih araligindaki sorgularda Between kullanma. Eger tarih/saat degelerinin saat bilgisi icermediginden %100 emin olabiliyorsan o zaman between kullanmakta sakinca yok. Ancak genelde bu tip kayitlar sadece tarihi degil tarih ve saati icerir (birisi gunun birinde senin "Siparis Gonderme Tarihi"ni saati de bilelim diye degistirirse ister istemez hatali sonuclar almaya baslarsin). BETWEEN yerine >= baslangic < bitis seklinde kullan. Bitis degeri biraz ozel, istedigin son degerden buyuk en kucuk deger. Yukaridaki ornekte 31 Mart 2012 degil, 1 Nisan 2012.  1 Nisan 2012 demek  1 Nisan 2012 00:00 demek. Sen de tum Mart ayindakileri istedigine gore 31 Mart 2012 23:59:59 da gonderilen kayit bile sonuclarda yer alacak demektir ki istedigimiz tam olarak bu.

    Diger bir boyutta bu sorguda 29.03.2012 31.03.2012 senin dedigin gibi bir datetimepicker'dan geliyor ve sen gereksiz yere once karaktere cevirip bu sorguyu hazirliyorsun. Oysa hicbir cevirim yapmamak daha dogru. DatetimePicker olduguna gore dili yuzde yuz kestirememekle birlikte bir .Net dili olabilmesi ihtimali var. O zaman da dogrudan soyle yap derim:

    1) Veri tabaninda karakter olarak ve degistirme ihtimali yok ise:

    DateTime baslangic = dtpBaslangic.Value;
    DateTime bitis = dtpBitis.Value.Date.AddDays(1); // 31 Mart secildiyse 1 Nisan yap
    
    DataTable tbl = new DataTable();
    using (SqlConnection con = new SqlConnection(...))
    {
      SqlCommand cmd = new SqlCommand(
      "select * from tabloAdi where convert(datetime, SipGondermeTarihi, 104) => @baslangic and convert(datetime, SipGondermeTarihi, 104) < @bitis", con);
      cmd.Parameters.AddWithValue( "@baslangic", baslangic);
      cmd.Parameters.AddWithValue( "@bitis", bitis);
      con.Open();
      tbl.Load( cmd.ExecuteReader() );
      con.Close();
    }
    
     

    2) Veri tabaninda ideal olarak Date ya da DateTime tipi kullandiysan, ve daha da iyisi kodunda Linq kullaniyorsan:

    DateTime baslangic = dtpBaslangic.Value;
    DateTime bitis = dtpBitis.Value.Date.AddDays(1); // 31 Mart secildiyse 1 Nisan yap
    
    var sorgu = from veri in db.TabloAdi
       where veri.SipGondermeTarihi >= baslangic &&
       veri.SipGondermeTarihi < bitis
       select veri;
    
    // sorgu bir IQueryable ve henuz server'a gitmedi. 
    // Istersen ek filtre, siralama vs ekleyip kullanabilirsin
    // ornek:
    
    var sorguSirali = sorgu.SortBy( veri => veri.SipGondermeTarihi );
    
    // bir DataGridView'un varsa ornek olarak:
    dataGridView1.DataSource = sorguSirali;
    
    // sorguSirali.ToString() ile de SQL'i alabilirsin.
    // ya da db.GetCommand( sorguSirali ) ile daha detayli

    • Yanıt Olarak İşaretleyen Serkan Bark 9 Nisan 2012 Pazartesi 07:07
    5 Nisan 2012 Perşembe 11:46
  • CetinBasoz cevabın için tşk ederim, dediklerini dünde denedim yine sonuc alamadım "The conversion of a char data type to a datetime data type resulted in an out-of-range datetime value." bu hata ile karşılaştım.Amatörlükten dolayı böyle bir hata yaptım  :) şimdi de düzeltemem ancak bu şekilde çalışırsa çalışacak.
    5 Nisan 2012 Perşembe 16:46
  • Mesaji takip et. Sana soyledigim tehlikeyi soyluyor. 31.3.2012 buradan bakinca gecerli bir tarih, oysa ABD ayarlariyla calisan bir SQL server'da (ki varsayilan bu) hata demek. 31. ay yok.
    5 Nisan 2012 Perşembe 17:41
  • hocam mesajınızı 10 kere okudum :) veriyi convert ettigim gibi tekrar ceviriyorum ama sonuc aynı hata.Son mesajınızda dediginiz gibi sql server'im turkish olarak calışıyor ama hata yine aynı . 
    5 Nisan 2012 Perşembe 18:55
  • Yani olay basit aslinda, gecersiz olan kayitlarini duzelt ve datetime kullan. Gecici olarak soyle bir fonksiyon yaratip hatayi olusturan kayitlari bulma sansin var (fonksiyon her turlu durumu dusunmuyor, gg.aa.yyyy formatinda veri oldugunu varsayiyor - TSQL ile bunlari yapmak olum, CLR ile yapsan daha iyi)

    CREATE FUNCTION ValidateDate ( @check VARCHAR(10) )
    RETURNS BIT
    AS 
        BEGIN
            DECLARE @gun VARCHAR(2) ,
                @ay VARCHAR(10) ,
                @yil VARCHAR(10) ,
                @valid BIT;
            SET @gun = CAST(SUBSTRING(@check, 1, CHARINDEX('.', @check) - 1) AS INT);
            SET @check = SUBSTRING(@check, CHARINDEX('.', @check) + 1, 10);
            SET @ay = CAST(SUBSTRING(@check, 1, CHARINDEX('.', @check) - 1) AS INT);
            SET @yil = CAST(SUBSTRING(@check, CHARINDEX('.', @check) + 1, 10) AS INT);
    
            SET @valid = CASE WHEN @ay < 1
                                   OR @ay > 12 THEN 0
                              WHEN @gun < 1
                                   OR @gun > ( CASE WHEN @ay = 2
                                                    THEN CASE WHEN ( @yil % 4 = 0
                                                                  AND @yil % 100 <> 0
                                                                  )
                                                                  OR @yil % 400 = 0
                                                              THEN 29
                                                              ELSE 28
                                                         END
                                                    WHEN @ay IN ( 4, 6, 9, 11 )
                                                    THEN 30
                                                    ELSE 31
                                               END ) THEN 0
                              ELSE 1
                         END;
    
            RETURN @valid;
        END
    

    Bunun yarattiktan sonra:

    Select sipGondermeTarihi from tabloAdi where dbo.ValidateDate( sipGOndermeTarihi ) = 0

    Iyi sanslar.

    Not: Dedigim gibi mesela LinqPAd ile tum sipGondermeTarihi kayitlarini alsan .Net koduyla daha kolay kontrol edersin.

    • Yanıt Olarak İşaretleyen Serkan Bark 9 Nisan 2012 Pazartesi 07:07
    6 Nisan 2012 Cuma 13:50